home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2017 October / PCgo 10-2017 CD-ROM Germany.iso / nw.pak / Unnamed File 005231.txt < prev    next >
Encoding:
Text File  |  2015-07-29  |  145.7 KB  |  4,591 lines

  1. /*
  2.  * Copyright (C) 2013 Google Inc. All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions are
  6.  * met:
  7.  *
  8.  *     * Redistributions of source code must retain the above copyright
  9.  * notice, this list of conditions and the following disclaimer.
  10.  *     * Redistributions in binary form must reproduce the above
  11.  * copyright notice, this list of conditions and the following disclaimer
  12.  * in the documentation and/or other materials provided with the
  13.  * distribution.
  14.  *     * Neither the name of Google Inc. nor the names of its
  15.  * contributors may be used to endorse or promote products derived from
  16.  * this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. /**
  32.  * FIXME: ES5 strict mode check is suppressed due to multiple uses of arguments.callee.
  33.  * @fileoverview
  34.  * @suppress {es5Strict}
  35.  */
  36.  
  37. /**
  38.  * @param {InjectedScriptHostClass} InjectedScriptHost
  39.  * @param {Window} inspectedWindow
  40.  * @param {number} injectedScriptId
  41.  * @param {!InjectedScript} injectedScript
  42.  */
  43. (function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript) {
  44.  
  45. var TypeUtils = {
  46.     /**
  47.      * http://www.khronos.org/registry/typedarray/specs/latest/#7
  48.      * @const
  49.      * @type {!Array.<function(new:ArrayBufferView, (!ArrayBuffer|!ArrayBufferView), number=, number=)>}
  50.      */
  51.     _typedArrayClasses: (function(typeNames) {
  52.         var result = [];
  53.         for (var i = 0, n = typeNames.length; i < n; ++i) {
  54.             if (inspectedWindow[typeNames[i]])
  55.                 result.push(inspectedWindow[typeNames[i]]);
  56.         }
  57.         return result;
  58.     })(["Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "Float32Array", "Float64Array"]),
  59.  
  60.     /**
  61.      * @const
  62.      * @type {!Array.<string>}
  63.      */
  64.     _supportedPropertyPrefixes: ["webkit"],
  65.  
  66.     /**
  67.      * @param {*} array
  68.      * @return {function(new:ArrayBufferView, (!ArrayBuffer|!ArrayBufferView), number=, number=)|null}
  69.      */
  70.     typedArrayClass: function(array)
  71.     {
  72.         var classes = TypeUtils._typedArrayClasses;
  73.         for (var i = 0, n = classes.length; i < n; ++i) {
  74.             if (array instanceof classes[i])
  75.                 return classes[i];
  76.         }
  77.         return null;
  78.     },
  79.  
  80.     /**
  81.      * @param {*} obj
  82.      * @return {*}
  83.      */
  84.     clone: function(obj)
  85.     {
  86.         if (!obj)
  87.             return obj;
  88.  
  89.         var type = typeof obj;
  90.         if (type !== "object" && type !== "function")
  91.             return obj;
  92.  
  93.         // Handle Array and ArrayBuffer instances.
  94.         if (typeof obj.slice === "function") {
  95.             console.assert(obj instanceof Array || obj instanceof ArrayBuffer);
  96.             return obj.slice(0);
  97.         }
  98.  
  99.         var typedArrayClass = TypeUtils.typedArrayClass(obj);
  100.         if (typedArrayClass)
  101.             return new typedArrayClass(/** @type {!ArrayBufferView} */ (obj));
  102.  
  103.         if (obj instanceof HTMLImageElement) {
  104.             var img = /** @type {!HTMLImageElement} */ (obj);
  105.             // Special case for Images with Blob URIs: cloneNode will fail if the Blob URI has already been revoked.
  106.             // FIXME: Maybe this is a bug in WebKit core?
  107.             if (/^blob:/.test(img.src))
  108.                 return TypeUtils.cloneIntoCanvas(img);
  109.             return img.cloneNode(true);
  110.         }
  111.  
  112.         if (obj instanceof HTMLCanvasElement)
  113.             return TypeUtils.cloneIntoCanvas(obj);
  114.  
  115.         if (obj instanceof HTMLVideoElement)
  116.             return TypeUtils.cloneIntoCanvas(obj, obj.videoWidth, obj.videoHeight);
  117.  
  118.         if (obj instanceof ImageData) {
  119.             var context = TypeUtils._dummyCanvas2dContext();
  120.             // FIXME: suppress type checks due to outdated builtin externs for createImageData.
  121.             var result = (/** @type {?} */ (context)).createImageData(obj);
  122.             for (var i = 0, n = obj.data.length; i < n; ++i)
  123.               result.data[i] = obj.data[i];
  124.             return result;
  125.         }
  126.  
  127.         // Try to convert to a primitive value via valueOf().
  128.         if (typeof obj.valueOf === "function") {
  129.             var value = obj.valueOf();
  130.             var valueType = typeof value;
  131.             if (valueType !== "object" && valueType !== "function")
  132.                 return value;
  133.         }
  134.  
  135.         console.error("ASSERT_NOT_REACHED: failed to clone object: ", obj);
  136.         return obj;
  137.     },
  138.  
  139.     /**
  140.      * @param {!HTMLImageElement|!HTMLCanvasElement|!HTMLVideoElement} obj
  141.      * @param {number=} width
  142.      * @param {number=} height
  143.      * @return {!HTMLCanvasElement}
  144.      */
  145.     cloneIntoCanvas: function(obj, width, height)
  146.     {
  147.         var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas"));
  148.         canvas.width = width || +obj.width;
  149.         canvas.height = height || +obj.height;
  150.         var context = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d")));
  151.         context.drawImage(obj, 0, 0);
  152.         return canvas;
  153.     },
  154.  
  155.     /**
  156.      * @param {?Object=} obj
  157.      * @return {?Object}
  158.      */
  159.     cloneObject: function(obj)
  160.     {
  161.         if (!obj)
  162.             return null;
  163.         var result = {};
  164.         for (var key in obj)
  165.             result[key] = obj[key];
  166.         return result;
  167.     },
  168.  
  169.     /**
  170.      * @param {!Array.<string>} names
  171.      * @return {!Object.<string, boolean>}
  172.      */
  173.     createPrefixedPropertyNamesSet: function(names)
  174.     {
  175.         var result = Object.create(null);
  176.         for (var i = 0, name; name = names[i]; ++i) {
  177.             result[name] = true;
  178.             var suffix = name.substr(0, 1).toUpperCase() + name.substr(1);
  179.             for (var j = 0, prefix; prefix = TypeUtils._supportedPropertyPrefixes[j]; ++j)
  180.                 result[prefix + suffix] = true;
  181.         }
  182.         return result;
  183.     },
  184.  
  185.     /**
  186.      * @return {number}
  187.      */
  188.     now: function()
  189.     {
  190.         try {
  191.             return inspectedWindow.performance.now();
  192.         } catch(e) {
  193.             try {
  194.                 return Date.now();
  195.             } catch(ex) {
  196.             }
  197.         }
  198.         return 0;
  199.     },
  200.  
  201.     /**
  202.      * @param {string} property
  203.      * @param {!Object} obj
  204.      * @return {boolean}
  205.      */
  206.     isEnumPropertyName: function(property, obj)
  207.     {
  208.         return (/^[A-Z][A-Z0-9_]+$/.test(property) && typeof obj[property] === "number");
  209.     },
  210.  
  211.     /**
  212.      * @return {!CanvasRenderingContext2D}
  213.      */
  214.     _dummyCanvas2dContext: function()
  215.     {
  216.         var context = TypeUtils._dummyCanvas2dContextInstance;
  217.         if (!context) {
  218.             var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas"));
  219.             context = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d")));
  220.             TypeUtils._dummyCanvas2dContextInstance = context;
  221.         }
  222.         return context;
  223.     }
  224. }
  225.  
  226. /** @typedef {{name:string, valueIsEnum:(boolean|undefined), value:*, values:(!Array.<!TypeUtils.InternalResourceStateDescriptor>|undefined), isArray:(boolean|undefined)}} */
  227. TypeUtils.InternalResourceStateDescriptor;
  228.  
  229. /**
  230.  * @interface
  231.  */
  232. function StackTrace()
  233. {
  234. }
  235.  
  236. StackTrace.prototype = {
  237.     /**
  238.      * @param {number} index
  239.      * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|undefined}
  240.      */
  241.     callFrame: function(index)
  242.     {
  243.     }
  244. }
  245.  
  246. /**
  247.  * @param {number=} stackTraceLimit
  248.  * @param {?Function=} topMostFunctionToIgnore
  249.  * @return {?StackTrace}
  250.  */
  251. StackTrace.create = function(stackTraceLimit, topMostFunctionToIgnore)
  252. {
  253.     if (typeof Error.captureStackTrace === "function")
  254.         return new StackTraceV8(stackTraceLimit, topMostFunctionToIgnore || arguments.callee);
  255.     // FIXME: Support JSC, and maybe other browsers.
  256.     return null;
  257. }
  258.  
  259. /**
  260.  * @constructor
  261.  * @implements {StackTrace}
  262.  * @param {number=} stackTraceLimit
  263.  * @param {?Function=} topMostFunctionToIgnore
  264.  * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
  265.  */
  266. function StackTraceV8(stackTraceLimit, topMostFunctionToIgnore)
  267. {
  268.     var oldPrepareStackTrace = Error.prepareStackTrace;
  269.     var oldStackTraceLimit = Error.stackTraceLimit;
  270.     if (typeof stackTraceLimit === "number")
  271.         Error.stackTraceLimit = stackTraceLimit;
  272.  
  273.     /**
  274.      * @param {!Object} error
  275.      * @param {!Array.<!CallSite>} structuredStackTrace
  276.      * @return {!Array.<{sourceURL: string, lineNumber: number, columnNumber: number}>}
  277.      */
  278.     Error.prepareStackTrace = function(error, structuredStackTrace)
  279.     {
  280.         return structuredStackTrace.map(function(callSite) {
  281.             return {
  282.                 sourceURL: callSite.getFileName(),
  283.                 lineNumber: callSite.getLineNumber(),
  284.                 columnNumber: callSite.getColumnNumber()
  285.             };
  286.         });
  287.     }
  288.  
  289.     var holder = /** @type {{stack: !Array.<{sourceURL: string, lineNumber: number, columnNumber: number}>}} */ ({});
  290.     Error.captureStackTrace(holder, topMostFunctionToIgnore || arguments.callee);
  291.     this._stackTrace = holder.stack;
  292.  
  293.     Error.stackTraceLimit = oldStackTraceLimit;
  294.     Error.prepareStackTrace = oldPrepareStackTrace;
  295. }
  296.  
  297. StackTraceV8.prototype = {
  298.     /**
  299.      * @override
  300.      * @param {number} index
  301.      * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|undefined}
  302.      */
  303.     callFrame: function(index)
  304.     {
  305.         return this._stackTrace[index];
  306.     }
  307. }
  308.  
  309. /**
  310.  * @constructor
  311.  * @template T
  312.  */
  313. function Cache()
  314. {
  315.     this.reset();
  316. }
  317.  
  318. Cache.prototype = {
  319.     /**
  320.      * @return {number}
  321.      */
  322.     size: function()
  323.     {
  324.         return this._size;
  325.     },
  326.  
  327.     reset: function()
  328.     {
  329.         /** @type {!Object.<number, !T>} */
  330.         this._items = Object.create(null);
  331.         /** @type {number} */
  332.         this._size = 0;
  333.     },
  334.  
  335.     /**
  336.      * @param {number} key
  337.      * @return {boolean}
  338.      */
  339.     has: function(key)
  340.     {
  341.         return key in this._items;
  342.     },
  343.  
  344.     /**
  345.      * @param {number} key
  346.      * @return {T|undefined}
  347.      */
  348.     get: function(key)
  349.     {
  350.         return this._items[key];
  351.     },
  352.  
  353.     /**
  354.      * @param {number} key
  355.      * @param {!T} item
  356.      */
  357.     put: function(key, item)
  358.     {
  359.         if (!this.has(key))
  360.             ++this._size;
  361.         this._items[key] = item;
  362.     }
  363. }
  364.  
  365. /**
  366.  * @constructor
  367.  * @param {?Resource|!Object} thisObject
  368.  * @param {string} functionName
  369.  * @param {!Array|!Arguments} args
  370.  * @param {!Resource|*=} result
  371.  * @param {?StackTrace=} stackTrace
  372.  */
  373. function Call(thisObject, functionName, args, result, stackTrace)
  374. {
  375.     this._thisObject = thisObject;
  376.     this._functionName = functionName;
  377.     this._args = Array.prototype.slice.call(args, 0);
  378.     this._result = result;
  379.     this._stackTrace = stackTrace || null;
  380.  
  381.     if (!this._functionName)
  382.         console.assert(this._args.length === 2 && typeof this._args[0] === "string");
  383. }
  384.  
  385. Call.prototype = {
  386.     /**
  387.      * @return {?Resource}
  388.      */
  389.     resource: function()
  390.     {
  391.         return Resource.forObject(this._thisObject);
  392.     },
  393.  
  394.     /**
  395.      * @return {string}
  396.      */
  397.     functionName: function()
  398.     {
  399.         return this._functionName;
  400.     },
  401.  
  402.     /**
  403.      * @return {boolean}
  404.      */
  405.     isPropertySetter: function()
  406.     {
  407.         return !this._functionName;
  408.     },
  409.  
  410.     /**
  411.      * @return {!Array}
  412.      */
  413.     args: function()
  414.     {
  415.         return this._args;
  416.     },
  417.  
  418.     /**
  419.      * @return {*}
  420.      */
  421.     result: function()
  422.     {
  423.         return this._result;
  424.     },
  425.  
  426.     /**
  427.      * @return {?StackTrace}
  428.      */
  429.     stackTrace: function()
  430.     {
  431.         return this._stackTrace;
  432.     },
  433.  
  434.     /**
  435.      * @param {?StackTrace} stackTrace
  436.      */
  437.     setStackTrace: function(stackTrace)
  438.     {
  439.         this._stackTrace = stackTrace;
  440.     },
  441.  
  442.     /**
  443.      * @param {*} result
  444.      */
  445.     setResult: function(result)
  446.     {
  447.         this._result = result;
  448.     },
  449.  
  450.     /**
  451.      * @param {string} name
  452.      * @param {?Object} attachment
  453.      */
  454.     setAttachment: function(name, attachment)
  455.     {
  456.         if (attachment) {
  457.             /** @type {?Object.<string, !Object>|undefined} */
  458.             this._attachments = this._attachments || Object.create(null);
  459.             this._attachments[name] = attachment;
  460.         } else if (this._attachments) {
  461.             delete this._attachments[name];
  462.         }
  463.     },
  464.  
  465.     /**
  466.      * @param {string} name
  467.      * @return {?Object}
  468.      */
  469.     attachment: function(name)
  470.     {
  471.         return this._attachments ? (this._attachments[name] || null) : null;
  472.     },
  473.  
  474.     freeze: function()
  475.     {
  476.         if (this._freezed)
  477.             return;
  478.         this._freezed = true;
  479.         for (var i = 0, n = this._args.length; i < n; ++i) {
  480.             // FIXME: freeze the Resources also!
  481.             if (!Resource.forObject(this._args[i]))
  482.                 this._args[i] = TypeUtils.clone(this._args[i]);
  483.         }
  484.     },
  485.  
  486.     /**
  487.      * @param {!Cache.<!ReplayableResource>} cache
  488.      * @return {!ReplayableCall}
  489.      */
  490.     toReplayable: function(cache)
  491.     {
  492.         this.freeze();
  493.         var thisObject = /** @type {!ReplayableResource} */ (Resource.toReplayable(this._thisObject, cache));
  494.         var result = Resource.toReplayable(this._result, cache);
  495.         var args = this._args.map(function(obj) {
  496.             return Resource.toReplayable(obj, cache);
  497.         });
  498.         var attachments = TypeUtils.cloneObject(this._attachments);
  499.         return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace, attachments);
  500.     },
  501.  
  502.     /**
  503.      * @param {!ReplayableCall} replayableCall
  504.      * @param {!Cache.<!Resource>} cache
  505.      * @return {!Call}
  506.      */
  507.     replay: function(replayableCall, cache)
  508.     {
  509.         var replayableResult = replayableCall.result();
  510.         if (replayableResult instanceof ReplayableResource && !cache.has(replayableResult.id())) {
  511.             var resource = replayableResult.replay(cache);
  512.             console.assert(resource.calls().length > 0, "Expected create* call for the Resource");
  513.             return resource.calls()[0];
  514.         }
  515.  
  516.         var replayObject = ReplayableResource.replay(replayableCall.replayableResource(), cache);
  517.         var replayArgs = replayableCall.args().map(function(obj) {
  518.             return ReplayableResource.replay(obj, cache);
  519.         });
  520.         var replayResult = undefined;
  521.  
  522.         if (replayableCall.isPropertySetter())
  523.             replayObject[replayArgs[0]] = replayArgs[1];
  524.         else {
  525.             var replayFunction = replayObject[replayableCall.functionName()];
  526.             console.assert(typeof replayFunction === "function", "Expected a function to replay");
  527.             replayResult = replayFunction.apply(replayObject, replayArgs);
  528.  
  529.             if (replayableResult instanceof ReplayableResource) {
  530.                 var resource = replayableResult.replay(cache);
  531.                 if (!resource.wrappedObject())
  532.                     resource.setWrappedObject(replayResult);
  533.             }
  534.         }
  535.  
  536.         this._thisObject = replayObject;
  537.         this._functionName = replayableCall.functionName();
  538.         this._args = replayArgs;
  539.         this._result = replayResult;
  540.         this._stackTrace = replayableCall.stackTrace();
  541.         this._freezed = true;
  542.         var attachments = replayableCall.attachments();
  543.         this._attachments = attachments ? TypeUtils.cloneObject(attachments) : null;
  544.         return this;
  545.     }
  546. }
  547.  
  548. /**
  549.  * @constructor
  550.  * @param {!ReplayableResource} thisObject
  551.  * @param {string} functionName
  552.  * @param {!Array.<!ReplayableResource|*>} args
  553.  * @param {!ReplayableResource|*} result
  554.  * @param {?StackTrace} stackTrace
  555.  * @param {?Object.<string, !Object>} attachments
  556.  */
  557. function ReplayableCall(thisObject, functionName, args, result, stackTrace, attachments)
  558. {
  559.     this._thisObject = thisObject;
  560.     this._functionName = functionName;
  561.     this._args = args;
  562.     this._result = result;
  563.     this._stackTrace = stackTrace;
  564.     if (attachments)
  565.         this._attachments = attachments;
  566. }
  567.  
  568. ReplayableCall.prototype = {
  569.     /**
  570.      * @return {!ReplayableResource}
  571.      */
  572.     replayableResource: function()
  573.     {
  574.         return this._thisObject;
  575.     },
  576.  
  577.     /**
  578.      * @return {string}
  579.      */
  580.     functionName: function()
  581.     {
  582.         return this._functionName;
  583.     },
  584.  
  585.     /**
  586.      * @return {boolean}
  587.      */
  588.     isPropertySetter: function()
  589.     {
  590.         return !this._functionName;
  591.     },
  592.  
  593.     /**
  594.      * @return {string}
  595.      */
  596.     propertyName: function()
  597.     {
  598.         console.assert(this.isPropertySetter());
  599.         return /** @type {string} */ (this._args[0]);
  600.     },
  601.  
  602.     /**
  603.      * @return {*}
  604.      */
  605.     propertyValue: function()
  606.     {
  607.         console.assert(this.isPropertySetter());
  608.         return this._args[1];
  609.     },
  610.  
  611.     /**
  612.      * @return {!Array.<!ReplayableResource|*>}
  613.      */
  614.     args: function()
  615.     {
  616.         return this._args;
  617.     },
  618.  
  619.     /**
  620.      * @return {!ReplayableResource|*}
  621.      */
  622.     result: function()
  623.     {
  624.         return this._result;
  625.     },
  626.  
  627.     /**
  628.      * @return {?StackTrace}
  629.      */
  630.     stackTrace: function()
  631.     {
  632.         return this._stackTrace;
  633.     },
  634.  
  635.     /**
  636.      * @return {?Object.<string, !Object>}
  637.      */
  638.     attachments: function()
  639.     {
  640.         return this._attachments || null;
  641.     },
  642.  
  643.     /**
  644.      * @param {string} name
  645.      * @return {!Object}
  646.      */
  647.     attachment: function(name)
  648.     {
  649.         return this._attachments && this._attachments[name];
  650.     },
  651.  
  652.     /**
  653.      * @param {!Cache.<!Resource>} cache
  654.      * @return {!Call}
  655.      */
  656.     replay: function(cache)
  657.     {
  658.         var call = /** @type {!Call} */ (Object.create(Call.prototype));
  659.         return call.replay(this, cache);
  660.     }
  661. }
  662.  
  663. /**
  664.  * @constructor
  665.  * @param {!Object} wrappedObject
  666.  * @param {string} name
  667.  */
  668. function Resource(wrappedObject, name)
  669. {
  670.     /** @type {number} */
  671.     this._id = ++Resource._uniqueId;
  672.     /** @type {string} */
  673.     this._name = name || "Resource";
  674.     /** @type {number} */
  675.     this._kindId = Resource._uniqueKindIds[this._name] = (Resource._uniqueKindIds[this._name] || 0) + 1;
  676.     /** @type {?ResourceTrackingManager} */
  677.     this._resourceManager = null;
  678.     /** @type {!Array.<!Call>} */
  679.     this._calls = [];
  680.     /**
  681.      * This is to prevent GC from collecting associated resources.
  682.      * Otherwise, for example in WebGL, subsequent calls to gl.getParameter()
  683.      * may return a recently created instance that is no longer bound to a
  684.      * Resource object (thus, no history to replay it later).
  685.      *
  686.      * @type {!Object.<string, !Resource>}
  687.      */
  688.     this._boundResources = Object.create(null);
  689.     this.setWrappedObject(wrappedObject);
  690. }
  691.  
  692. /**
  693.  * @type {number}
  694.  */
  695. Resource._uniqueId = 0;
  696.  
  697. /**
  698.  * @type {!Object.<string, number>}
  699.  */
  700. Resource._uniqueKindIds = {};
  701.  
  702. /**
  703.  * @param {*} obj
  704.  * @return {?Resource}
  705.  */
  706. Resource.forObject = function(obj)
  707. {
  708.     if (!obj)
  709.         return null;
  710.     if (obj instanceof Resource)
  711.         return obj;
  712.     if (typeof obj === "object")
  713.         return obj["__resourceObject"];
  714.     return null;
  715. }
  716.  
  717. /**
  718.  * @param {!Resource|*} obj
  719.  * @return {*}
  720.  */
  721. Resource.wrappedObject = function(obj)
  722. {
  723.     var resource = Resource.forObject(obj);
  724.     return resource ? resource.wrappedObject() : obj;
  725. }
  726.  
  727. /**
  728.  * @param {!Resource|*} obj
  729.  * @param {!Cache.<!ReplayableResource>} cache
  730.  * @return {!ReplayableResource|*}
  731.  */
  732. Resource.toReplayable = function(obj, cache)
  733. {
  734.     var resource = Resource.forObject(obj);
  735.     return resource ? resource.toReplayable(cache) : obj;
  736. }
  737.  
  738. Resource.prototype = {
  739.     /**
  740.      * @return {number}
  741.      */
  742.     id: function()
  743.     {
  744.         return this._id;
  745.     },
  746.  
  747.     /**
  748.      * @return {string}
  749.      */
  750.     name: function()
  751.     {
  752.         return this._name;
  753.     },
  754.  
  755.     /**
  756.      * @return {string}
  757.      */
  758.     description: function()
  759.     {
  760.         return this._name + "@" + this._kindId;
  761.     },
  762.  
  763.     /**
  764.      * @return {!Object}
  765.      */
  766.     wrappedObject: function()
  767.     {
  768.         return this._wrappedObject;
  769.     },
  770.  
  771.     /**
  772.      * @param {!Object} value
  773.      */
  774.     setWrappedObject: function(value)
  775.     {
  776.         console.assert(value, "wrappedObject should not be NULL");
  777.         console.assert(!(value instanceof Resource), "Binding a Resource object to another Resource object?");
  778.         this._wrappedObject = value;
  779.         this._bindObjectToResource(value);
  780.     },
  781.  
  782.     /**
  783.      * @return {!Object}
  784.      */
  785.     proxyObject: function()
  786.     {
  787.         if (!this._proxyObject)
  788.             this._proxyObject = this._wrapObject();
  789.         return this._proxyObject;
  790.     },
  791.  
  792.     /**
  793.      * @return {?ResourceTrackingManager}
  794.      */
  795.     manager: function()
  796.     {
  797.         return this._resourceManager;
  798.     },
  799.  
  800.     /**
  801.      * @param {!ResourceTrackingManager} value
  802.      */
  803.     setManager: function(value)
  804.     {
  805.         this._resourceManager = value;
  806.     },
  807.  
  808.     /**
  809.      * @return {!Array.<!Call>}
  810.      */
  811.     calls: function()
  812.     {
  813.         return this._calls;
  814.     },
  815.  
  816.     /**
  817.      * @return {?ContextResource}
  818.      */
  819.     contextResource: function()
  820.     {
  821.         if (this instanceof ContextResource)
  822.             return /** @type {!ContextResource} */ (this);
  823.  
  824.         if (this._calculatingContextResource)
  825.             return null;
  826.  
  827.         this._calculatingContextResource = true;
  828.         var result = null;
  829.         for (var i = 0, n = this._calls.length; i < n; ++i) {
  830.             result = this._calls[i].resource().contextResource();
  831.             if (result)
  832.                 break;
  833.         }
  834.         delete this._calculatingContextResource;
  835.         console.assert(result, "Failed to find context resource for " + this._name + "@" + this._kindId);
  836.         return result;
  837.     },
  838.  
  839.     /**
  840.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  841.      */
  842.     currentState: function()
  843.     {
  844.         var result = [];
  845.         var proxyObject = this.proxyObject();
  846.         if (!proxyObject)
  847.             return result;
  848.         var statePropertyNames = this._proxyStatePropertyNames || [];
  849.         for (var i = 0, n = statePropertyNames.length; i < n; ++i) {
  850.             var pname = statePropertyNames[i];
  851.             result.push({ name: pname, value: proxyObject[pname] });
  852.         }
  853.         result.push({ name: "context", value: this.contextResource() });
  854.         return result;
  855.     },
  856.  
  857.     /**
  858.      * @return {string}
  859.      */
  860.     toDataURL: function()
  861.     {
  862.         return "";
  863.     },
  864.  
  865.     /**
  866.      * @param {!Cache.<!ReplayableResource>} cache
  867.      * @return {!ReplayableResource}
  868.      */
  869.     toReplayable: function(cache)
  870.     {
  871.         var result = cache.get(this._id);
  872.         if (result)
  873.             return result;
  874.         var data = {
  875.             id: this._id,
  876.             name: this._name,
  877.             kindId: this._kindId
  878.         };
  879.         result = new ReplayableResource(this, data);
  880.         cache.put(this._id, result); // Put into the cache early to avoid loops.
  881.         data.calls = this._calls.map(function(call) {
  882.             return call.toReplayable(cache);
  883.         });
  884.         this._populateReplayableData(data, cache);
  885.         var contextResource = this.contextResource();
  886.         if (contextResource !== this)
  887.             data.contextResource = Resource.toReplayable(contextResource, cache);
  888.         return result;
  889.     },
  890.  
  891.     /**
  892.      * @param {!Object} data
  893.      * @param {!Cache.<!ReplayableResource>} cache
  894.      */
  895.     _populateReplayableData: function(data, cache)
  896.     {
  897.         // Do nothing. Should be overridden by subclasses.
  898.     },
  899.  
  900.     /**
  901.      * @param {!Object} data
  902.      * @param {!Cache.<!Resource>} cache
  903.      * @return {!Resource}
  904.      */
  905.     replay: function(data, cache)
  906.     {
  907.         var resource = cache.get(data.id);
  908.         if (resource)
  909.             return resource;
  910.         this._id = data.id;
  911.         this._name = data.name;
  912.         this._kindId = data.kindId;
  913.         this._resourceManager = null;
  914.         this._calls = [];
  915.         this._boundResources = Object.create(null);
  916.         this._wrappedObject = null;
  917.         cache.put(data.id, this); // Put into the cache early to avoid loops.
  918.         this._doReplayCalls(data, cache);
  919.         console.assert(this._wrappedObject, "Resource should be reconstructed!");
  920.         return this;
  921.     },
  922.  
  923.     /**
  924.      * @param {!Object} data
  925.      * @param {!Cache.<!Resource>} cache
  926.      */
  927.     _doReplayCalls: function(data, cache)
  928.     {
  929.         for (var i = 0, n = data.calls.length; i < n; ++i)
  930.             this._calls.push(data.calls[i].replay(cache));
  931.     },
  932.  
  933.     /**
  934.      * @param {!Call} call
  935.      */
  936.     pushCall: function(call)
  937.     {
  938.         call.freeze();
  939.         this._calls.push(call);
  940.     },
  941.  
  942.     /**
  943.      * @param {!Call} call
  944.      */
  945.     onCallReplayed: function(call)
  946.     {
  947.         // Ignore by default.
  948.     },
  949.  
  950.     /**
  951.      * @param {!Object} object
  952.      */
  953.     _bindObjectToResource: function(object)
  954.     {
  955.         Object.defineProperty(object, "__resourceObject", {
  956.             value: this,
  957.             writable: false,
  958.             enumerable: false,
  959.             configurable: true
  960.         });
  961.     },
  962.  
  963.     /**
  964.      * @param {string} key
  965.      * @param {*} obj
  966.      */
  967.     _registerBoundResource: function(key, obj)
  968.     {
  969.         var resource = Resource.forObject(obj);
  970.         if (resource)
  971.             this._boundResources[key] = resource;
  972.         else
  973.             delete this._boundResources[key];
  974.     },
  975.  
  976.     /**
  977.      * @return {?Object}
  978.      */
  979.     _wrapObject: function()
  980.     {
  981.         var wrappedObject = this.wrappedObject();
  982.         if (!wrappedObject)
  983.             return null;
  984.         var proxy = Object.create(wrappedObject.__proto__); // In order to emulate "instanceof".
  985.  
  986.         var customWrapFunctions = this._customWrapFunctions();
  987.         /** @type {!Array.<string>} */
  988.         this._proxyStatePropertyNames = [];
  989.  
  990.         /**
  991.          * @param {string} property
  992.          * @this {Resource}
  993.          */
  994.         function processProperty(property)
  995.         {
  996.             if (typeof wrappedObject[property] === "function") {
  997.                 var customWrapFunction = customWrapFunctions[property];
  998.                 if (customWrapFunction)
  999.                     proxy[property] = this._wrapCustomFunction(this, wrappedObject, wrappedObject[property], property, customWrapFunction);
  1000.                 else
  1001.                     proxy[property] = this._wrapFunction(this, wrappedObject, wrappedObject[property], property);
  1002.             } else if (TypeUtils.isEnumPropertyName(property, wrappedObject)) {
  1003.                 // Fast access to enums and constants.
  1004.                 proxy[property] = wrappedObject[property];
  1005.             } else {
  1006.                 this._proxyStatePropertyNames.push(property);
  1007.                 Object.defineProperty(proxy, property, {
  1008.                     get: function()
  1009.                     {
  1010.                         var obj = wrappedObject[property];
  1011.                         var resource = Resource.forObject(obj);
  1012.                         return resource ? resource : obj;
  1013.                     },
  1014.                     set: this._wrapPropertySetter(this, wrappedObject, property),
  1015.                     enumerable: true
  1016.                 });
  1017.             }
  1018.         }
  1019.  
  1020.         var isEmpty = true;
  1021.         for (var property in wrappedObject) {
  1022.             isEmpty = false;
  1023.             processProperty.call(this, property);
  1024.         }
  1025.         if (isEmpty)
  1026.             return wrappedObject; // Nothing to proxy.
  1027.  
  1028.         this._bindObjectToResource(proxy);
  1029.         return proxy;
  1030.     },
  1031.  
  1032.     /**
  1033.      * @param {!Resource} resource
  1034.      * @param {!Object} originalObject
  1035.      * @param {!Function} originalFunction
  1036.      * @param {string} functionName
  1037.      * @param {!Function} customWrapFunction
  1038.      * @return {!Function}
  1039.      */
  1040.     _wrapCustomFunction: function(resource, originalObject, originalFunction, functionName, customWrapFunction)
  1041.     {
  1042.         return function()
  1043.         {
  1044.             var manager = resource.manager();
  1045.             var isCapturing = manager && manager.capturing();
  1046.             if (isCapturing)
  1047.                 manager.captureArguments(resource, arguments);
  1048.             var wrapFunction = new Resource.WrapFunction(originalObject, originalFunction, functionName, arguments);
  1049.             customWrapFunction.apply(wrapFunction, arguments);
  1050.             if (isCapturing) {
  1051.                 var call = wrapFunction.call();
  1052.                 call.setStackTrace(StackTrace.create(1, arguments.callee));
  1053.                 manager.captureCall(call);
  1054.             }
  1055.             return wrapFunction.result();
  1056.         };
  1057.     },
  1058.  
  1059.     /**
  1060.      * @param {!Resource} resource
  1061.      * @param {!Object} originalObject
  1062.      * @param {!Function} originalFunction
  1063.      * @param {string} functionName
  1064.      * @return {!Function}
  1065.      */
  1066.     _wrapFunction: function(resource, originalObject, originalFunction, functionName)
  1067.     {
  1068.         return function()
  1069.         {
  1070.             var manager = resource.manager();
  1071.             if (!manager || !manager.capturing())
  1072.                 return originalFunction.apply(originalObject, arguments);
  1073.             manager.captureArguments(resource, arguments);
  1074.             var result = originalFunction.apply(originalObject, arguments);
  1075.             var stackTrace = StackTrace.create(1, arguments.callee);
  1076.             var call = new Call(resource, functionName, arguments, result, stackTrace);
  1077.             manager.captureCall(call);
  1078.             return result;
  1079.         };
  1080.     },
  1081.  
  1082.     /**
  1083.      * @param {!Resource} resource
  1084.      * @param {!Object} originalObject
  1085.      * @param {string} propertyName
  1086.      * @return {function(*)}
  1087.      */
  1088.     _wrapPropertySetter: function(resource, originalObject, propertyName)
  1089.     {
  1090.         return function(value)
  1091.         {
  1092.             resource._registerBoundResource(propertyName, value);
  1093.             var manager = resource.manager();
  1094.             if (!manager || !manager.capturing()) {
  1095.                 originalObject[propertyName] = Resource.wrappedObject(value);
  1096.                 return;
  1097.             }
  1098.             var args = [propertyName, value];
  1099.             manager.captureArguments(resource, args);
  1100.             originalObject[propertyName] = Resource.wrappedObject(value);
  1101.             var stackTrace = StackTrace.create(1, arguments.callee);
  1102.             var call = new Call(resource, "", args, undefined, stackTrace);
  1103.             manager.captureCall(call);
  1104.         };
  1105.     },
  1106.  
  1107.     /**
  1108.      * @return {!Object.<string, !Function>}
  1109.      */
  1110.     _customWrapFunctions: function()
  1111.     {
  1112.         return Object.create(null); // May be overridden by subclasses.
  1113.     }
  1114. }
  1115.  
  1116. /**
  1117.  * @constructor
  1118.  * @param {!Object} originalObject
  1119.  * @param {!Function} originalFunction
  1120.  * @param {string} functionName
  1121.  * @param {!Array|!Arguments} args
  1122.  */
  1123. Resource.WrapFunction = function(originalObject, originalFunction, functionName, args)
  1124. {
  1125.     this._originalObject = originalObject;
  1126.     this._originalFunction = originalFunction;
  1127.     this._functionName = functionName;
  1128.     this._args = args;
  1129.     this._resource = Resource.forObject(originalObject);
  1130.     console.assert(this._resource, "Expected a wrapped call on a Resource object.");
  1131. }
  1132.  
  1133. Resource.WrapFunction.prototype = {
  1134.     /**
  1135.      * @return {*}
  1136.      */
  1137.     result: function()
  1138.     {
  1139.         if (!this._executed) {
  1140.             this._executed = true;
  1141.             this._result = this._originalFunction.apply(this._originalObject, this._args);
  1142.         }
  1143.         return this._result;
  1144.     },
  1145.  
  1146.     /**
  1147.      * @return {!Call}
  1148.      */
  1149.     call: function()
  1150.     {
  1151.         if (!this._call)
  1152.             this._call = new Call(this._resource, this._functionName, this._args, this.result());
  1153.         return this._call;
  1154.     },
  1155.  
  1156.     /**
  1157.      * @param {*} result
  1158.      */
  1159.     overrideResult: function(result)
  1160.     {
  1161.         var call = this.call();
  1162.         call.setResult(result);
  1163.         this._result = result;
  1164.     }
  1165. }
  1166.  
  1167. /**
  1168.  * @param {function(new:Resource, !Object, string)} resourceConstructor
  1169.  * @param {string} resourceName
  1170.  * @return {function(this:Resource.WrapFunction)}
  1171.  */
  1172. Resource.WrapFunction.resourceFactoryMethod = function(resourceConstructor, resourceName)
  1173. {
  1174.     return /** @this {Resource.WrapFunction} */ function()
  1175.     {
  1176.         var wrappedObject = /** @type {?Object} */ (this.result());
  1177.         if (!wrappedObject)
  1178.             return;
  1179.         var resource = new resourceConstructor(wrappedObject, resourceName);
  1180.         var manager = this._resource.manager();
  1181.         if (manager)
  1182.             manager.registerResource(resource);
  1183.         this.overrideResult(resource.proxyObject());
  1184.         resource.pushCall(this.call());
  1185.     }
  1186. }
  1187.  
  1188. /**
  1189.  * @constructor
  1190.  * @param {!Resource} originalResource
  1191.  * @param {!Object} data
  1192.  */
  1193. function ReplayableResource(originalResource, data)
  1194. {
  1195.     this._proto = originalResource.__proto__;
  1196.     this._data = data;
  1197. }
  1198.  
  1199. ReplayableResource.prototype = {
  1200.     /**
  1201.      * @return {number}
  1202.      */
  1203.     id: function()
  1204.     {
  1205.         return this._data.id;
  1206.     },
  1207.  
  1208.     /**
  1209.      * @return {string}
  1210.      */
  1211.     name: function()
  1212.     {
  1213.         return this._data.name;
  1214.     },
  1215.  
  1216.     /**
  1217.      * @return {string}
  1218.      */
  1219.     description: function()
  1220.     {
  1221.         return this._data.name + "@" + this._data.kindId;
  1222.     },
  1223.  
  1224.     /**
  1225.      * @return {!ReplayableResource}
  1226.      */
  1227.     contextResource: function()
  1228.     {
  1229.         return this._data.contextResource || this;
  1230.     },
  1231.  
  1232.     /**
  1233.      * @param {!Cache.<!Resource>} cache
  1234.      * @return {!Resource}
  1235.      */
  1236.     replay: function(cache)
  1237.     {
  1238.         var result = /** @type {!Resource} */ (Object.create(this._proto));
  1239.         result = result.replay(this._data, cache)
  1240.         console.assert(result.__proto__ === this._proto, "Wrong type of a replay result");
  1241.         return result;
  1242.     }
  1243. }
  1244.  
  1245. /**
  1246.  * @param {!ReplayableResource|*} obj
  1247.  * @param {!Cache.<!Resource>} cache
  1248.  * @return {*}
  1249.  */
  1250. ReplayableResource.replay = function(obj, cache)
  1251. {
  1252.     return (obj instanceof ReplayableResource) ? obj.replay(cache).wrappedObject() : obj;
  1253. }
  1254.  
  1255. /**
  1256.  * @constructor
  1257.  * @extends {Resource}
  1258.  * @param {!Object} wrappedObject
  1259.  * @param {string} name
  1260.  */
  1261. function ContextResource(wrappedObject, name)
  1262. {
  1263.     Resource.call(this, wrappedObject, name);
  1264. }
  1265.  
  1266. ContextResource.prototype = {
  1267.     __proto__: Resource.prototype
  1268. }
  1269.  
  1270. /**
  1271.  * @constructor
  1272.  * @extends {Resource}
  1273.  * @param {!Object} wrappedObject
  1274.  * @param {string} name
  1275.  */
  1276. function LogEverythingResource(wrappedObject, name)
  1277. {
  1278.     Resource.call(this, wrappedObject, name);
  1279. }
  1280.  
  1281. LogEverythingResource.prototype = {
  1282.     /**
  1283.      * @override
  1284.      * @return {!Object.<string, !Function>}
  1285.      */
  1286.     _customWrapFunctions: function()
  1287.     {
  1288.         var wrapFunctions = Object.create(null);
  1289.         var wrappedObject = this.wrappedObject();
  1290.         if (wrappedObject) {
  1291.             for (var property in wrappedObject) {
  1292.                 /** @this {Resource.WrapFunction} */
  1293.                 wrapFunctions[property] = function()
  1294.                 {
  1295.                     this._resource.pushCall(this.call());
  1296.                 }
  1297.             }
  1298.         }
  1299.         return wrapFunctions;
  1300.     },
  1301.  
  1302.     __proto__: Resource.prototype
  1303. }
  1304.  
  1305. ////////////////////////////////////////////////////////////////////////////////
  1306. // WebGL
  1307. ////////////////////////////////////////////////////////////////////////////////
  1308.  
  1309. /**
  1310.  * @constructor
  1311.  * @extends {Resource}
  1312.  * @param {!Object} wrappedObject
  1313.  * @param {string} name
  1314.  */
  1315. function WebGLBoundResource(wrappedObject, name)
  1316. {
  1317.     Resource.call(this, wrappedObject, name);
  1318.     /** @type {!Object.<string, *>} */
  1319.     this._state = {};
  1320. }
  1321.  
  1322. WebGLBoundResource.prototype = {
  1323.     /**
  1324.      * @override
  1325.      * @param {!Object} data
  1326.      * @param {!Cache.<!ReplayableResource>} cache
  1327.      */
  1328.     _populateReplayableData: function(data, cache)
  1329.     {
  1330.         var state = this._state;
  1331.         data.state = {};
  1332.         Object.keys(state).forEach(function(parameter) {
  1333.             data.state[parameter] = Resource.toReplayable(state[parameter], cache);
  1334.         });
  1335.     },
  1336.  
  1337.     /**
  1338.      * @override
  1339.      * @param {!Object} data
  1340.      * @param {!Cache.<!Resource>} cache
  1341.      */
  1342.     _doReplayCalls: function(data, cache)
  1343.     {
  1344.         var gl = this._replayContextResource(data, cache).wrappedObject();
  1345.  
  1346.         /** @type {!Object.<string, !Array.<string>>} */
  1347.         var bindingsData = {
  1348.             TEXTURE_2D: ["bindTexture", "TEXTURE_BINDING_2D"],
  1349.             TEXTURE_CUBE_MAP: ["bindTexture", "TEXTURE_BINDING_CUBE_MAP"],
  1350.             ARRAY_BUFFER: ["bindBuffer", "ARRAY_BUFFER_BINDING"],
  1351.             ELEMENT_ARRAY_BUFFER: ["bindBuffer", "ELEMENT_ARRAY_BUFFER_BINDING"],
  1352.             FRAMEBUFFER: ["bindFramebuffer", "FRAMEBUFFER_BINDING"],
  1353.             RENDERBUFFER: ["bindRenderbuffer", "RENDERBUFFER_BINDING"]
  1354.         };
  1355.         var originalBindings = {};
  1356.         Object.keys(bindingsData).forEach(function(bindingTarget) {
  1357.             var bindingParameter = bindingsData[bindingTarget][1];
  1358.             originalBindings[bindingTarget] = gl.getParameter(gl[bindingParameter]);
  1359.         });
  1360.  
  1361.         var state = {};
  1362.         Object.keys(data.state).forEach(function(parameter) {
  1363.             state[parameter] = ReplayableResource.replay(data.state[parameter], cache);
  1364.         });
  1365.         this._state = state;
  1366.         Resource.prototype._doReplayCalls.call(this, data, cache);
  1367.  
  1368.         Object.keys(bindingsData).forEach(function(bindingTarget) {
  1369.             var bindMethodName = bindingsData[bindingTarget][0];
  1370.             gl[bindMethodName].call(gl, gl[bindingTarget], originalBindings[bindingTarget]);
  1371.         });
  1372.     },
  1373.  
  1374.     /**
  1375.      * @param {!Object} data
  1376.      * @param {!Cache.<!Resource>} cache
  1377.      * @return {?WebGLRenderingContextResource}
  1378.      */
  1379.     _replayContextResource: function(data, cache)
  1380.     {
  1381.         var calls = /** @type {!Array.<!ReplayableCall>} */ (data.calls);
  1382.         for (var i = 0, n = calls.length; i < n; ++i) {
  1383.             var resource = ReplayableResource.replay(calls[i].replayableResource(), cache);
  1384.             var contextResource = WebGLRenderingContextResource.forObject(resource);
  1385.             if (contextResource)
  1386.                 return contextResource;
  1387.         }
  1388.         return null;
  1389.     },
  1390.  
  1391.     /**
  1392.      * @param {number} target
  1393.      * @param {string} bindMethodName
  1394.      */
  1395.     pushBinding: function(target, bindMethodName)
  1396.     {
  1397.         if (this._state.bindTarget !== target) {
  1398.             this._state.bindTarget = target;
  1399.             this.pushCall(new Call(WebGLRenderingContextResource.forObject(this), bindMethodName, [target, this]));
  1400.         }
  1401.     },
  1402.  
  1403.     __proto__: Resource.prototype
  1404. }
  1405.  
  1406. /**
  1407.  * @constructor
  1408.  * @extends {WebGLBoundResource}
  1409.  * @param {!Object} wrappedObject
  1410.  * @param {string} name
  1411.  */
  1412. function WebGLTextureResource(wrappedObject, name)
  1413. {
  1414.     WebGLBoundResource.call(this, wrappedObject, name);
  1415. }
  1416.  
  1417. WebGLTextureResource.prototype = {
  1418.     /**
  1419.      * @override (overrides @return type)
  1420.      * @return {!WebGLTexture}
  1421.      */
  1422.     wrappedObject: function()
  1423.     {
  1424.         return this._wrappedObject;
  1425.     },
  1426.  
  1427.     /**
  1428.      * @override
  1429.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  1430.      */
  1431.     currentState: function()
  1432.     {
  1433.         var result = [];
  1434.         var glResource = WebGLRenderingContextResource.forObject(this);
  1435.         var gl = glResource.wrappedObject();
  1436.         var texture = this.wrappedObject();
  1437.         if (!gl || !texture)
  1438.             return result;
  1439.         result.push({ name: "isTexture", value: gl.isTexture(texture) });
  1440.         result.push({ name: "context", value: this.contextResource() });
  1441.  
  1442.         var target = this._state.bindTarget;
  1443.         if (typeof target !== "number")
  1444.             return result;
  1445.  
  1446.         var bindingParameter;
  1447.         switch (target) {
  1448.         case gl.TEXTURE_2D:
  1449.             bindingParameter = gl.TEXTURE_BINDING_2D;
  1450.             break;
  1451.         case gl.TEXTURE_CUBE_MAP:
  1452.             bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP;
  1453.             break;
  1454.         default:
  1455.             console.error("ASSERT_NOT_REACHED: unknown texture target " + target);
  1456.             return result;
  1457.         }
  1458.         result.push({ name: "target", value: target, valueIsEnum: true });
  1459.  
  1460.         var oldTexture = /** @type {!WebGLTexture} */ (gl.getParameter(bindingParameter));
  1461.         if (oldTexture !== texture)
  1462.             gl.bindTexture(target, texture);
  1463.  
  1464.         var textureParameters = [
  1465.             "TEXTURE_MAG_FILTER",
  1466.             "TEXTURE_MIN_FILTER",
  1467.             "TEXTURE_WRAP_S",
  1468.             "TEXTURE_WRAP_T",
  1469.             "TEXTURE_MAX_ANISOTROPY_EXT" // EXT_texture_filter_anisotropic extension
  1470.         ];
  1471.         glResource.queryStateValues(gl.getTexParameter, target, textureParameters, result);
  1472.  
  1473.         if (oldTexture !== texture)
  1474.             gl.bindTexture(target, oldTexture);
  1475.         return result;
  1476.     },
  1477.  
  1478.     /**
  1479.      * @override
  1480.      * @param {!Object} data
  1481.      * @param {!Cache.<!Resource>} cache
  1482.      */
  1483.     _doReplayCalls: function(data, cache)
  1484.     {
  1485.         var gl = this._replayContextResource(data, cache).wrappedObject();
  1486.  
  1487.         var state = {};
  1488.         WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1489.             state[parameter] = gl.getParameter(gl[parameter]);
  1490.         });
  1491.  
  1492.         WebGLBoundResource.prototype._doReplayCalls.call(this, data, cache);
  1493.  
  1494.         WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1495.             gl.pixelStorei(gl[parameter], state[parameter]);
  1496.         });
  1497.     },
  1498.  
  1499.     /**
  1500.      * @override
  1501.      * @param {!Call} call
  1502.      */
  1503.     pushCall: function(call)
  1504.     {
  1505.         var gl = WebGLRenderingContextResource.forObject(call.resource()).wrappedObject();
  1506.         WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1507.             var value = gl.getParameter(gl[parameter]);
  1508.             if (this._state[parameter] !== value) {
  1509.                 this._state[parameter] = value;
  1510.                 var pixelStoreCall = new Call(gl, "pixelStorei", [gl[parameter], value]);
  1511.                 WebGLBoundResource.prototype.pushCall.call(this, pixelStoreCall);
  1512.             }
  1513.         }, this);
  1514.  
  1515.         // FIXME: remove any older calls that no longer contribute to the resource state.
  1516.         // FIXME: optimize memory usage: maybe it's more efficient to store one texImage2D call instead of many texSubImage2D.
  1517.         WebGLBoundResource.prototype.pushCall.call(this, call);
  1518.     },
  1519.  
  1520.     /**
  1521.      * Handles: texParameteri, texParameterf
  1522.      * @param {!Call} call
  1523.      */
  1524.     pushCall_texParameter: function(call)
  1525.     {
  1526.         var args = call.args();
  1527.         var pname = args[1];
  1528.         var param = args[2];
  1529.         if (this._state[pname] !== param) {
  1530.             this._state[pname] = param;
  1531.             WebGLBoundResource.prototype.pushCall.call(this, call);
  1532.         }
  1533.     },
  1534.  
  1535.     /**
  1536.      * Handles: copyTexImage2D, copyTexSubImage2D
  1537.      * copyTexImage2D and copyTexSubImage2D define a texture image with pixels from the current framebuffer.
  1538.      * @param {!Call} call
  1539.      */
  1540.     pushCall_copyTexImage2D: function(call)
  1541.     {
  1542.         var glResource = WebGLRenderingContextResource.forObject(call.resource());
  1543.         var gl = glResource.wrappedObject();
  1544.         var framebufferResource = /** @type {!WebGLFramebufferResource} */ (glResource.currentBinding(gl.FRAMEBUFFER));
  1545.         if (framebufferResource)
  1546.             this.pushCall(new Call(glResource, "bindFramebuffer", [gl.FRAMEBUFFER, framebufferResource]));
  1547.         else {
  1548.             // FIXME: Implement this case.
  1549.             console.error("ASSERT_NOT_REACHED: Could not properly process a gl." + call.functionName() + " call while the DRAWING BUFFER is bound.");
  1550.         }
  1551.         this.pushCall(call);
  1552.     },
  1553.  
  1554.     __proto__: WebGLBoundResource.prototype
  1555. }
  1556.  
  1557. /**
  1558.  * @constructor
  1559.  * @extends {Resource}
  1560.  * @param {!Object} wrappedObject
  1561.  * @param {string} name
  1562.  */
  1563. function WebGLProgramResource(wrappedObject, name)
  1564. {
  1565.     Resource.call(this, wrappedObject, name);
  1566. }
  1567.  
  1568. WebGLProgramResource.prototype = {
  1569.     /**
  1570.      * @override (overrides @return type)
  1571.      * @return {!WebGLProgram}
  1572.      */
  1573.     wrappedObject: function()
  1574.     {
  1575.         return this._wrappedObject;
  1576.     },
  1577.  
  1578.     /**
  1579.      * @override
  1580.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  1581.      */
  1582.     currentState: function()
  1583.     {
  1584.         /**
  1585.          * @param {!Object} obj
  1586.          * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output
  1587.          */
  1588.         function convertToStateDescriptors(obj, output)
  1589.         {
  1590.             for (var pname in obj)
  1591.                 output.push({ name: pname, value: obj[pname], valueIsEnum: (pname === "type") });
  1592.         }
  1593.  
  1594.         var result = [];
  1595.         var program = this.wrappedObject();
  1596.         if (!program)
  1597.             return result;
  1598.         var glResource = WebGLRenderingContextResource.forObject(this);
  1599.         var gl = glResource.wrappedObject();
  1600.         var programParameters = ["DELETE_STATUS", "LINK_STATUS", "VALIDATE_STATUS"];
  1601.         glResource.queryStateValues(gl.getProgramParameter, program, programParameters, result);
  1602.         result.push({ name: "getProgramInfoLog", value: gl.getProgramInfoLog(program) });
  1603.         result.push({ name: "isProgram", value: gl.isProgram(program) });
  1604.         result.push({ name: "context", value: this.contextResource() });
  1605.  
  1606.         // ATTACHED_SHADERS
  1607.         var callFormatter = CallFormatter.forResource(this);
  1608.         var shaders = gl.getAttachedShaders(program) || [];
  1609.         var shaderDescriptors = [];
  1610.         for (var i = 0, n = shaders.length; i < n; ++i) {
  1611.             var shaderResource = Resource.forObject(shaders[i]);
  1612.             var pname = callFormatter.enumNameForValue(shaderResource.type());
  1613.             shaderDescriptors.push({ name: pname, value: shaderResource });
  1614.         }
  1615.         result.push({ name: "ATTACHED_SHADERS", values: shaderDescriptors, isArray: true });
  1616.  
  1617.         // ACTIVE_UNIFORMS
  1618.         var uniformDescriptors = [];
  1619.         var uniforms = this._activeUniforms(true);
  1620.         for (var i = 0, n = uniforms.length; i < n; ++i) {
  1621.             var pname = "" + i;
  1622.             var values = [];
  1623.             convertToStateDescriptors(uniforms[i], values);
  1624.             uniformDescriptors.push({ name: pname, values: values });
  1625.         }
  1626.         result.push({ name: "ACTIVE_UNIFORMS", values: uniformDescriptors, isArray: true });
  1627.  
  1628.         // ACTIVE_ATTRIBUTES
  1629.         var attributesCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES));
  1630.         var attributeDescriptors = [];
  1631.         for (var i = 0; i < attributesCount; ++i) {
  1632.             var activeInfo = gl.getActiveAttrib(program, i);
  1633.             if (!activeInfo)
  1634.                 continue;
  1635.             var pname = "" + i;
  1636.             var values = [];
  1637.             convertToStateDescriptors(activeInfo, values);
  1638.             attributeDescriptors.push({ name: pname, values: values });
  1639.         }
  1640.         result.push({ name: "ACTIVE_ATTRIBUTES", values: attributeDescriptors, isArray: true });
  1641.  
  1642.         return result;
  1643.     },
  1644.  
  1645.     /**
  1646.      * @param {boolean=} includeAllInfo
  1647.      * @return {!Array.<{name:string, type:number, value:*, size:(number|undefined)}>}
  1648.      */
  1649.     _activeUniforms: function(includeAllInfo)
  1650.     {
  1651.         var uniforms = [];
  1652.         var program = this.wrappedObject();
  1653.         if (!program)
  1654.             return uniforms;
  1655.  
  1656.         var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
  1657.         var uniformsCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS));
  1658.         for (var i = 0; i < uniformsCount; ++i) {
  1659.             var activeInfo = gl.getActiveUniform(program, i);
  1660.             if (!activeInfo)
  1661.                 continue;
  1662.             var uniformLocation = gl.getUniformLocation(program, activeInfo.name);
  1663.             if (!uniformLocation)
  1664.                 continue;
  1665.             var value = gl.getUniform(program, uniformLocation);
  1666.             var item = Object.create(null);
  1667.             item.name = activeInfo.name;
  1668.             item.type = activeInfo.type;
  1669.             item.value = value;
  1670.             if (includeAllInfo)
  1671.                 item.size = activeInfo.size;
  1672.             uniforms.push(item);
  1673.         }
  1674.         return uniforms;
  1675.     },
  1676.  
  1677.     /**
  1678.      * @override
  1679.      * @param {!Object} data
  1680.      * @param {!Cache.<!ReplayableResource>} cache
  1681.      */
  1682.     _populateReplayableData: function(data, cache)
  1683.     {
  1684.         var glResource = WebGLRenderingContextResource.forObject(this);
  1685.         var originalErrors = glResource.getAllErrors();
  1686.         data.uniforms = this._activeUniforms();
  1687.         glResource.restoreErrors(originalErrors);
  1688.     },
  1689.  
  1690.     /**
  1691.      * @override
  1692.      * @param {!Object} data
  1693.      * @param {!Cache.<!Resource>} cache
  1694.      */
  1695.     _doReplayCalls: function(data, cache)
  1696.     {
  1697.         Resource.prototype._doReplayCalls.call(this, data, cache);
  1698.         var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
  1699.         var program = this.wrappedObject();
  1700.  
  1701.         var originalProgram = /** @type {!WebGLProgram} */ (gl.getParameter(gl.CURRENT_PROGRAM));
  1702.         var currentProgram = originalProgram;
  1703.  
  1704.         data.uniforms.forEach(function(uniform) {
  1705.             var uniformLocation = gl.getUniformLocation(program, uniform.name);
  1706.             if (!uniformLocation)
  1707.                 return;
  1708.             if (currentProgram !== program) {
  1709.                 currentProgram = program;
  1710.                 gl.useProgram(program);
  1711.             }
  1712.             var methodName = this._uniformMethodNameByType(gl, uniform.type);
  1713.             if (methodName.indexOf("Matrix") === -1)
  1714.                 gl[methodName].call(gl, uniformLocation, uniform.value);
  1715.             else
  1716.                 gl[methodName].call(gl, uniformLocation, false, uniform.value);
  1717.         }.bind(this));
  1718.  
  1719.         if (currentProgram !== originalProgram)
  1720.             gl.useProgram(originalProgram);
  1721.     },
  1722.  
  1723.     /**
  1724.      * @param {!WebGLRenderingContext} gl
  1725.      * @param {number} type
  1726.      * @return {string}
  1727.      */
  1728.     _uniformMethodNameByType: function(gl, type)
  1729.     {
  1730.         var uniformMethodNames = WebGLProgramResource._uniformMethodNames;
  1731.         if (!uniformMethodNames) {
  1732.             uniformMethodNames = {};
  1733.             uniformMethodNames[gl.FLOAT] = "uniform1f";
  1734.             uniformMethodNames[gl.FLOAT_VEC2] = "uniform2fv";
  1735.             uniformMethodNames[gl.FLOAT_VEC3] = "uniform3fv";
  1736.             uniformMethodNames[gl.FLOAT_VEC4] = "uniform4fv";
  1737.             uniformMethodNames[gl.INT] = "uniform1i";
  1738.             uniformMethodNames[gl.BOOL] = "uniform1i";
  1739.             uniformMethodNames[gl.SAMPLER_2D] = "uniform1i";
  1740.             uniformMethodNames[gl.SAMPLER_CUBE] = "uniform1i";
  1741.             uniformMethodNames[gl.INT_VEC2] = "uniform2iv";
  1742.             uniformMethodNames[gl.BOOL_VEC2] = "uniform2iv";
  1743.             uniformMethodNames[gl.INT_VEC3] = "uniform3iv";
  1744.             uniformMethodNames[gl.BOOL_VEC3] = "uniform3iv";
  1745.             uniformMethodNames[gl.INT_VEC4] = "uniform4iv";
  1746.             uniformMethodNames[gl.BOOL_VEC4] = "uniform4iv";
  1747.             uniformMethodNames[gl.FLOAT_MAT2] = "uniformMatrix2fv";
  1748.             uniformMethodNames[gl.FLOAT_MAT3] = "uniformMatrix3fv";
  1749.             uniformMethodNames[gl.FLOAT_MAT4] = "uniformMatrix4fv";
  1750.             WebGLProgramResource._uniformMethodNames = uniformMethodNames;
  1751.         }
  1752.         console.assert(uniformMethodNames[type], "Unknown uniform type " + type);
  1753.         return uniformMethodNames[type];
  1754.     },
  1755.  
  1756.     /**
  1757.      * @override
  1758.      * @param {!Call} call
  1759.      */
  1760.     pushCall: function(call)
  1761.     {
  1762.         // FIXME: remove any older calls that no longer contribute to the resource state.
  1763.         // FIXME: handle multiple attachShader && detachShader.
  1764.         Resource.prototype.pushCall.call(this, call);
  1765.     },
  1766.  
  1767.     __proto__: Resource.prototype
  1768. }
  1769.  
  1770. /**
  1771.  * @constructor
  1772.  * @extends {Resource}
  1773.  * @param {!Object} wrappedObject
  1774.  * @param {string} name
  1775.  */
  1776. function WebGLShaderResource(wrappedObject, name)
  1777. {
  1778.     Resource.call(this, wrappedObject, name);
  1779. }
  1780.  
  1781. WebGLShaderResource.prototype = {
  1782.     /**
  1783.      * @override (overrides @return type)
  1784.      * @return {!WebGLShader}
  1785.      */
  1786.     wrappedObject: function()
  1787.     {
  1788.         return this._wrappedObject;
  1789.     },
  1790.  
  1791.     /**
  1792.      * @return {number}
  1793.      */
  1794.     type: function()
  1795.     {
  1796.         var call = this._calls[0];
  1797.         if (call && call.functionName() === "createShader")
  1798.             return call.args()[0];
  1799.         console.error("ASSERT_NOT_REACHED: Failed to restore shader type from the log.", call);
  1800.         return 0;
  1801.     },
  1802.  
  1803.     /**
  1804.      * @override
  1805.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  1806.      */
  1807.     currentState: function()
  1808.     {
  1809.         var result = [];
  1810.         var shader = this.wrappedObject();
  1811.         if (!shader)
  1812.             return result;
  1813.         var glResource = WebGLRenderingContextResource.forObject(this);
  1814.         var gl = glResource.wrappedObject();
  1815.         var shaderParameters = ["SHADER_TYPE", "DELETE_STATUS", "COMPILE_STATUS"];
  1816.         glResource.queryStateValues(gl.getShaderParameter, shader, shaderParameters, result);
  1817.         result.push({ name: "getShaderInfoLog", value: gl.getShaderInfoLog(shader) });
  1818.         result.push({ name: "getShaderSource", value: gl.getShaderSource(shader) });
  1819.         result.push({ name: "isShader", value: gl.isShader(shader) });
  1820.         result.push({ name: "context", value: this.contextResource() });
  1821.  
  1822.         // getShaderPrecisionFormat
  1823.         var shaderType = this.type();
  1824.         var precisionValues = [];
  1825.         var precisionParameters = ["LOW_FLOAT", "MEDIUM_FLOAT", "HIGH_FLOAT", "LOW_INT", "MEDIUM_INT", "HIGH_INT"];
  1826.         for (var i = 0, pname; pname = precisionParameters[i]; ++i)
  1827.             precisionValues.push({ name: pname, value: gl.getShaderPrecisionFormat(shaderType, gl[pname]) });
  1828.         result.push({ name: "getShaderPrecisionFormat", values: precisionValues });
  1829.  
  1830.         return result;
  1831.     },
  1832.  
  1833.     /**
  1834.      * @override
  1835.      * @param {!Call} call
  1836.      */
  1837.     pushCall: function(call)
  1838.     {
  1839.         // FIXME: remove any older calls that no longer contribute to the resource state.
  1840.         // FIXME: handle multiple shaderSource calls.
  1841.         Resource.prototype.pushCall.call(this, call);
  1842.     },
  1843.  
  1844.     __proto__: Resource.prototype
  1845. }
  1846.  
  1847. /**
  1848.  * @constructor
  1849.  * @extends {WebGLBoundResource}
  1850.  * @param {!Object} wrappedObject
  1851.  * @param {string} name
  1852.  */
  1853. function WebGLBufferResource(wrappedObject, name)
  1854. {
  1855.     WebGLBoundResource.call(this, wrappedObject, name);
  1856. }
  1857.  
  1858. WebGLBufferResource.prototype = {
  1859.     /**
  1860.      * @override (overrides @return type)
  1861.      * @return {!WebGLBuffer}
  1862.      */
  1863.     wrappedObject: function()
  1864.     {
  1865.         return this._wrappedObject;
  1866.     },
  1867.  
  1868.     /**
  1869.      * @return {?ArrayBufferView}
  1870.      */
  1871.     cachedBufferData: function()
  1872.     {
  1873.         /**
  1874.          * Creates a view to a given buffer, does NOT copy the buffer.
  1875.          * @param {!ArrayBuffer|!ArrayBufferView} buffer
  1876.          * @return {!Uint8Array}
  1877.          */
  1878.         function createUint8ArrayBufferView(buffer)
  1879.         {
  1880.             return buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  1881.         }
  1882.  
  1883.         if (!this._cachedBufferData) {
  1884.             for (var i = this._calls.length - 1; i >= 0; --i) {
  1885.                 var call = this._calls[i];
  1886.                 if (call.functionName() === "bufferData") {
  1887.                     var sizeOrData = /** @type {number|!ArrayBuffer|!ArrayBufferView} */ (call.args()[1]);
  1888.                     if (typeof sizeOrData === "number")
  1889.                         this._cachedBufferData = new ArrayBuffer(sizeOrData);
  1890.                     else
  1891.                         this._cachedBufferData = sizeOrData;
  1892.                     this._lastBufferSubDataIndex = i + 1;
  1893.                     break;
  1894.                 }
  1895.             }
  1896.             if (!this._cachedBufferData)
  1897.                 return null;
  1898.         }
  1899.  
  1900.         // Apply any "bufferSubData" calls that have not been applied yet.
  1901.         var bufferDataView;
  1902.         while (this._lastBufferSubDataIndex < this._calls.length) {
  1903.             var call = this._calls[this._lastBufferSubDataIndex++];
  1904.             if (call.functionName() !== "bufferSubData")
  1905.                 continue;
  1906.             var offset = /** @type {number} */ (call.args()[1]);
  1907.             var data = /** @type {!ArrayBuffer|!ArrayBufferView} */ (call.args()[2]);
  1908.             var view = createUint8ArrayBufferView(data);
  1909.             if (!bufferDataView)
  1910.                 bufferDataView = createUint8ArrayBufferView(this._cachedBufferData);
  1911.             bufferDataView.set(view, offset);
  1912.  
  1913.             var isFullReplacement = (offset === 0 && bufferDataView.length === view.length);
  1914.             if (this._cachedBufferData instanceof ArrayBuffer) {
  1915.                 // The buffer data has no type yet. Try to guess from the "bufferSubData" call.
  1916.                 var typedArrayClass = TypeUtils.typedArrayClass(data);
  1917.                 if (typedArrayClass)
  1918.                     this._cachedBufferData = new typedArrayClass(this._cachedBufferData); // Does not copy the buffer.
  1919.             } else if (isFullReplacement) {
  1920.                 var typedArrayClass = TypeUtils.typedArrayClass(data);
  1921.                 if (typedArrayClass) {
  1922.                     var typedArrayData = /** @type {!ArrayBufferView} */ (data);
  1923.                     this._cachedBufferData = new typedArrayClass(this._cachedBufferData.buffer, this._cachedBufferData.byteOffset, typedArrayData.length); // Does not copy the buffer.
  1924.                 }
  1925.             }
  1926.         }
  1927.  
  1928.         if (this._cachedBufferData instanceof ArrayBuffer) {
  1929.             // If we failed to guess the data type yet, use Uint8Array.
  1930.             return new Uint8Array(this._cachedBufferData);
  1931.         }
  1932.         return this._cachedBufferData;
  1933.     },
  1934.  
  1935.     /**
  1936.      * @override
  1937.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  1938.      */
  1939.     currentState: function()
  1940.     {
  1941.         var result = [];
  1942.         var glResource = WebGLRenderingContextResource.forObject(this);
  1943.         var gl = glResource.wrappedObject();
  1944.         var buffer = this.wrappedObject();
  1945.         if (!gl || !buffer)
  1946.             return result;
  1947.         result.push({ name: "isBuffer", value: gl.isBuffer(buffer) });
  1948.         result.push({ name: "context", value: this.contextResource() });
  1949.  
  1950.         var target = this._state.bindTarget;
  1951.         if (typeof target !== "number")
  1952.             return result;
  1953.  
  1954.         var bindingParameter;
  1955.         switch (target) {
  1956.         case gl.ARRAY_BUFFER:
  1957.             bindingParameter = gl.ARRAY_BUFFER_BINDING;
  1958.             break;
  1959.         case gl.ELEMENT_ARRAY_BUFFER:
  1960.             bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING;
  1961.             break;
  1962.         default:
  1963.             console.error("ASSERT_NOT_REACHED: unknown buffer target " + target);
  1964.             return result;
  1965.         }
  1966.         result.push({ name: "target", value: target, valueIsEnum: true });
  1967.  
  1968.         var oldBuffer = /** @type {!WebGLBuffer} */ (gl.getParameter(bindingParameter));
  1969.         if (oldBuffer !== buffer)
  1970.             gl.bindBuffer(target, buffer);
  1971.  
  1972.         var bufferParameters = ["BUFFER_SIZE", "BUFFER_USAGE"];
  1973.         glResource.queryStateValues(gl.getBufferParameter, target, bufferParameters, result);
  1974.  
  1975.         if (oldBuffer !== buffer)
  1976.             gl.bindBuffer(target, oldBuffer);
  1977.  
  1978.         try {
  1979.             var data = this.cachedBufferData();
  1980.             if (data)
  1981.                 result.push({ name: "bufferData", value: data });
  1982.         } catch (e) {
  1983.             console.error("Exception while restoring bufferData", e);
  1984.         }
  1985.  
  1986.         return result;
  1987.     },
  1988.  
  1989.     /**
  1990.      * @param {!Call} call
  1991.      */
  1992.     pushCall_bufferData: function(call)
  1993.     {
  1994.         // FIXME: remove any older calls that no longer contribute to the resource state.
  1995.         delete this._cachedBufferData;
  1996.         delete this._lastBufferSubDataIndex;
  1997.         WebGLBoundResource.prototype.pushCall.call(this, call);
  1998.     },
  1999.  
  2000.     /**
  2001.      * @param {!Call} call
  2002.      */
  2003.     pushCall_bufferSubData: function(call)
  2004.     {
  2005.         // FIXME: Optimize memory for bufferSubData.
  2006.         WebGLBoundResource.prototype.pushCall.call(this, call);
  2007.     },
  2008.  
  2009.     __proto__: WebGLBoundResource.prototype
  2010. }
  2011.  
  2012. /**
  2013.  * @constructor
  2014.  * @extends {WebGLBoundResource}
  2015.  * @param {!Object} wrappedObject
  2016.  * @param {string} name
  2017.  */
  2018. function WebGLFramebufferResource(wrappedObject, name)
  2019. {
  2020.     WebGLBoundResource.call(this, wrappedObject, name);
  2021. }
  2022.  
  2023. WebGLFramebufferResource.prototype = {
  2024.     /**
  2025.      * @override (overrides @return type)
  2026.      * @return {!WebGLFramebuffer}
  2027.      */
  2028.     wrappedObject: function()
  2029.     {
  2030.         return this._wrappedObject;
  2031.     },
  2032.  
  2033.     /**
  2034.      * @override
  2035.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  2036.      */
  2037.     currentState: function()
  2038.     {
  2039.         var result = [];
  2040.         var framebuffer = this.wrappedObject();
  2041.         if (!framebuffer)
  2042.             return result;
  2043.         var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
  2044.  
  2045.         var oldFramebuffer = /** @type {!WebGLFramebuffer} */ (gl.getParameter(gl.FRAMEBUFFER_BINDING));
  2046.         if (oldFramebuffer !== framebuffer)
  2047.             gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  2048.  
  2049.         var attachmentParameters = ["COLOR_ATTACHMENT0", "DEPTH_ATTACHMENT", "STENCIL_ATTACHMENT"];
  2050.         var framebufferParameters = ["FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE"];
  2051.         for (var i = 0, attachment; attachment = attachmentParameters[i]; ++i) {
  2052.             var values = [];
  2053.             for (var j = 0, pname; pname = framebufferParameters[j]; ++j) {
  2054.                 var value = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl[attachment], gl[pname]);
  2055.                 value = Resource.forObject(value) || value;
  2056.                 values.push({ name: pname, value: value, valueIsEnum: WebGLRenderingContextResource.GetResultIsEnum[pname] });
  2057.             }
  2058.             result.push({ name: attachment, values: values });
  2059.         }
  2060.         result.push({ name: "isFramebuffer", value: gl.isFramebuffer(framebuffer) });
  2061.         result.push({ name: "context", value: this.contextResource() });
  2062.  
  2063.         if (oldFramebuffer !== framebuffer)
  2064.             gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer);
  2065.         return result;
  2066.     },
  2067.  
  2068.     /**
  2069.      * @override
  2070.      * @param {!Call} call
  2071.      */
  2072.     pushCall: function(call)
  2073.     {
  2074.         // FIXME: remove any older calls that no longer contribute to the resource state.
  2075.         WebGLBoundResource.prototype.pushCall.call(this, call);
  2076.     },
  2077.  
  2078.     __proto__: WebGLBoundResource.prototype
  2079. }
  2080.  
  2081. /**
  2082.  * @constructor
  2083.  * @extends {WebGLBoundResource}
  2084.  * @param {!Object} wrappedObject
  2085.  * @param {string} name
  2086.  */
  2087. function WebGLRenderbufferResource(wrappedObject, name)
  2088. {
  2089.     WebGLBoundResource.call(this, wrappedObject, name);
  2090. }
  2091.  
  2092. WebGLRenderbufferResource.prototype = {
  2093.     /**
  2094.      * @override (overrides @return type)
  2095.      * @return {!WebGLRenderbuffer}
  2096.      */
  2097.     wrappedObject: function()
  2098.     {
  2099.         return this._wrappedObject;
  2100.     },
  2101.  
  2102.     /**
  2103.      * @override
  2104.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  2105.      */
  2106.     currentState: function()
  2107.     {
  2108.         var result = [];
  2109.         var renderbuffer = this.wrappedObject();
  2110.         if (!renderbuffer)
  2111.             return result;
  2112.         var glResource = WebGLRenderingContextResource.forObject(this);
  2113.         var gl = glResource.wrappedObject();
  2114.  
  2115.         var oldRenderbuffer = /** @type {!WebGLRenderbuffer} */ (gl.getParameter(gl.RENDERBUFFER_BINDING));
  2116.         if (oldRenderbuffer !== renderbuffer)
  2117.             gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
  2118.  
  2119.         var renderbufferParameters = ["RENDERBUFFER_WIDTH", "RENDERBUFFER_HEIGHT", "RENDERBUFFER_INTERNAL_FORMAT", "RENDERBUFFER_RED_SIZE", "RENDERBUFFER_GREEN_SIZE", "RENDERBUFFER_BLUE_SIZE", "RENDERBUFFER_ALPHA_SIZE", "RENDERBUFFER_DEPTH_SIZE", "RENDERBUFFER_STENCIL_SIZE"];
  2120.         glResource.queryStateValues(gl.getRenderbufferParameter, gl.RENDERBUFFER, renderbufferParameters, result);
  2121.         result.push({ name: "isRenderbuffer", value: gl.isRenderbuffer(renderbuffer) });
  2122.         result.push({ name: "context", value: this.contextResource() });
  2123.  
  2124.         if (oldRenderbuffer !== renderbuffer)
  2125.             gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbuffer);
  2126.         return result;
  2127.     },
  2128.  
  2129.     /**
  2130.      * @override
  2131.      * @param {!Call} call
  2132.      */
  2133.     pushCall: function(call)
  2134.     {
  2135.         // FIXME: remove any older calls that no longer contribute to the resource state.
  2136.         WebGLBoundResource.prototype.pushCall.call(this, call);
  2137.     },
  2138.  
  2139.     __proto__: WebGLBoundResource.prototype
  2140. }
  2141.  
  2142. /**
  2143.  * @constructor
  2144.  * @extends {Resource}
  2145.  * @param {!Object} wrappedObject
  2146.  * @param {string} name
  2147.  */
  2148. function WebGLUniformLocationResource(wrappedObject, name)
  2149. {
  2150.     Resource.call(this, wrappedObject, name);
  2151. }
  2152.  
  2153. WebGLUniformLocationResource.prototype = {
  2154.     /**
  2155.      * @override (overrides @return type)
  2156.      * @return {!WebGLUniformLocation}
  2157.      */
  2158.     wrappedObject: function()
  2159.     {
  2160.         return this._wrappedObject;
  2161.     },
  2162.  
  2163.     /**
  2164.      * @return {?WebGLProgramResource}
  2165.      */
  2166.     program: function()
  2167.     {
  2168.         var call = this._calls[0];
  2169.         if (call && call.functionName() === "getUniformLocation")
  2170.             return /** @type {!WebGLProgramResource} */ (Resource.forObject(call.args()[0]));
  2171.         console.error("ASSERT_NOT_REACHED: Failed to restore WebGLUniformLocation from the log.", call);
  2172.         return null;
  2173.     },
  2174.  
  2175.     /**
  2176.      * @return {string}
  2177.      */
  2178.     name: function()
  2179.     {
  2180.         var call = this._calls[0];
  2181.         if (call && call.functionName() === "getUniformLocation")
  2182.             return call.args()[1];
  2183.         console.error("ASSERT_NOT_REACHED: Failed to restore WebGLUniformLocation from the log.", call);
  2184.         return "";
  2185.     },
  2186.  
  2187.     /**
  2188.      * @override
  2189.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  2190.      */
  2191.     currentState: function()
  2192.     {
  2193.         var result = [];
  2194.         var location = this.wrappedObject();
  2195.         if (!location)
  2196.             return result;
  2197.         var programResource = this.program();
  2198.         var program = programResource && programResource.wrappedObject();
  2199.         if (!program)
  2200.             return result;
  2201.         var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
  2202.         var uniformValue = gl.getUniform(program, location);
  2203.         var name = this.name();
  2204.         result.push({ name: "name", value: name });
  2205.         result.push({ name: "program", value: programResource });
  2206.         result.push({ name: "value", value: uniformValue });
  2207.         result.push({ name: "context", value: this.contextResource() });
  2208.  
  2209.         if (typeof this._type !== "number") {
  2210.             var altName = name + "[0]";
  2211.             var uniformsCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS));
  2212.             for (var i = 0; i < uniformsCount; ++i) {
  2213.                 var activeInfo = gl.getActiveUniform(program, i);
  2214.                 if (!activeInfo)
  2215.                     continue;
  2216.                 if (activeInfo.name === name || activeInfo.name === altName) {
  2217.                     this._type = activeInfo.type;
  2218.                     this._size = activeInfo.size;
  2219.                     if (activeInfo.name === name)
  2220.                         break;
  2221.                 }
  2222.             }
  2223.         }
  2224.         if (typeof this._type === "number")
  2225.             result.push({ name: "type", value: this._type, valueIsEnum: true });
  2226.         if (typeof this._size === "number")
  2227.             result.push({ name: "size", value: this._size });
  2228.  
  2229.         return result;
  2230.     },
  2231.  
  2232.     /**
  2233.      * @override
  2234.      * @param {!Object} data
  2235.      * @param {!Cache.<!ReplayableResource>} cache
  2236.      */
  2237.     _populateReplayableData: function(data, cache)
  2238.     {
  2239.         data.type = this._type;
  2240.         data.size = this._size;
  2241.     },
  2242.  
  2243.     /**
  2244.      * @override
  2245.      * @param {!Object} data
  2246.      * @param {!Cache.<!Resource>} cache
  2247.      */
  2248.     _doReplayCalls: function(data, cache)
  2249.     {
  2250.         this._type = data.type;
  2251.         this._size = data.size;
  2252.         Resource.prototype._doReplayCalls.call(this, data, cache);
  2253.     },
  2254.  
  2255.     __proto__: Resource.prototype
  2256. }
  2257.  
  2258. /**
  2259.  * @constructor
  2260.  * @extends {ContextResource}
  2261.  * @param {!WebGLRenderingContext} glContext
  2262.  */
  2263. function WebGLRenderingContextResource(glContext)
  2264. {
  2265.     ContextResource.call(this, glContext, "WebGLRenderingContext");
  2266.     /** @type {?Object.<number, boolean>} */
  2267.     this._customErrors = null;
  2268.     /** @type {!Object.<string, string>} */
  2269.     this._extensions = {};
  2270.     /** @type {!Object.<string, number>} */
  2271.     this._extensionEnums = {};
  2272. }
  2273.  
  2274. /**
  2275.  * @const
  2276.  * @type {!Array.<string>}
  2277.  */
  2278. WebGLRenderingContextResource.GLCapabilities = [
  2279.     "BLEND",
  2280.     "CULL_FACE",
  2281.     "DEPTH_TEST",
  2282.     "DITHER",
  2283.     "POLYGON_OFFSET_FILL",
  2284.     "SAMPLE_ALPHA_TO_COVERAGE",
  2285.     "SAMPLE_COVERAGE",
  2286.     "SCISSOR_TEST",
  2287.     "STENCIL_TEST"
  2288. ];
  2289.  
  2290. /**
  2291.  * @const
  2292.  * @type {!Array.<string>}
  2293.  */
  2294. WebGLRenderingContextResource.PixelStoreParameters = [
  2295.     "PACK_ALIGNMENT",
  2296.     "UNPACK_ALIGNMENT",
  2297.     "UNPACK_COLORSPACE_CONVERSION_WEBGL",
  2298.     "UNPACK_FLIP_Y_WEBGL",
  2299.     "UNPACK_PREMULTIPLY_ALPHA_WEBGL"
  2300. ];
  2301.  
  2302. /**
  2303.  * @const
  2304.  * @type {!Array.<string>}
  2305.  */
  2306. WebGLRenderingContextResource.StateParameters = [
  2307.     "ACTIVE_TEXTURE",
  2308.     "ARRAY_BUFFER_BINDING",
  2309.     "BLEND_COLOR",
  2310.     "BLEND_DST_ALPHA",
  2311.     "BLEND_DST_RGB",
  2312.     "BLEND_EQUATION_ALPHA",
  2313.     "BLEND_EQUATION_RGB",
  2314.     "BLEND_SRC_ALPHA",
  2315.     "BLEND_SRC_RGB",
  2316.     "COLOR_CLEAR_VALUE",
  2317.     "COLOR_WRITEMASK",
  2318.     "CULL_FACE_MODE",
  2319.     "CURRENT_PROGRAM",
  2320.     "DEPTH_CLEAR_VALUE",
  2321.     "DEPTH_FUNC",
  2322.     "DEPTH_RANGE",
  2323.     "DEPTH_WRITEMASK",
  2324.     "ELEMENT_ARRAY_BUFFER_BINDING",
  2325.     "FRAGMENT_SHADER_DERIVATIVE_HINT_OES", // OES_standard_derivatives extension
  2326.     "FRAMEBUFFER_BINDING",
  2327.     "FRONT_FACE",
  2328.     "GENERATE_MIPMAP_HINT",
  2329.     "LINE_WIDTH",
  2330.     "PACK_ALIGNMENT",
  2331.     "POLYGON_OFFSET_FACTOR",
  2332.     "POLYGON_OFFSET_UNITS",
  2333.     "RENDERBUFFER_BINDING",
  2334.     "SAMPLE_COVERAGE_INVERT",
  2335.     "SAMPLE_COVERAGE_VALUE",
  2336.     "SCISSOR_BOX",
  2337.     "STENCIL_BACK_FAIL",
  2338.     "STENCIL_BACK_FUNC",
  2339.     "STENCIL_BACK_PASS_DEPTH_FAIL",
  2340.     "STENCIL_BACK_PASS_DEPTH_PASS",
  2341.     "STENCIL_BACK_REF",
  2342.     "STENCIL_BACK_VALUE_MASK",
  2343.     "STENCIL_BACK_WRITEMASK",
  2344.     "STENCIL_CLEAR_VALUE",
  2345.     "STENCIL_FAIL",
  2346.     "STENCIL_FUNC",
  2347.     "STENCIL_PASS_DEPTH_FAIL",
  2348.     "STENCIL_PASS_DEPTH_PASS",
  2349.     "STENCIL_REF",
  2350.     "STENCIL_VALUE_MASK",
  2351.     "STENCIL_WRITEMASK",
  2352.     "UNPACK_ALIGNMENT",
  2353.     "UNPACK_COLORSPACE_CONVERSION_WEBGL",
  2354.     "UNPACK_FLIP_Y_WEBGL",
  2355.     "UNPACK_PREMULTIPLY_ALPHA_WEBGL",
  2356.     "VERTEX_ARRAY_BINDING_OES", // OES_vertex_array_object extension
  2357.     "VIEWPORT"
  2358. ];
  2359.  
  2360. /**
  2361.  * True for those enums that return also an enum via a getter API method (e.g. getParameter, getShaderParameter, etc.).
  2362.  * @const
  2363.  * @type {!Object.<string, boolean>}
  2364.  */
  2365. WebGLRenderingContextResource.GetResultIsEnum = TypeUtils.createPrefixedPropertyNamesSet([
  2366.     // gl.getParameter()
  2367.     "ACTIVE_TEXTURE",
  2368.     "BLEND_DST_ALPHA",
  2369.     "BLEND_DST_RGB",
  2370.     "BLEND_EQUATION_ALPHA",
  2371.     "BLEND_EQUATION_RGB",
  2372.     "BLEND_SRC_ALPHA",
  2373.     "BLEND_SRC_RGB",
  2374.     "CULL_FACE_MODE",
  2375.     "DEPTH_FUNC",
  2376.     "FRONT_FACE",
  2377.     "GENERATE_MIPMAP_HINT",
  2378.     "FRAGMENT_SHADER_DERIVATIVE_HINT_OES",
  2379.     "STENCIL_BACK_FAIL",
  2380.     "STENCIL_BACK_FUNC",
  2381.     "STENCIL_BACK_PASS_DEPTH_FAIL",
  2382.     "STENCIL_BACK_PASS_DEPTH_PASS",
  2383.     "STENCIL_FAIL",
  2384.     "STENCIL_FUNC",
  2385.     "STENCIL_PASS_DEPTH_FAIL",
  2386.     "STENCIL_PASS_DEPTH_PASS",
  2387.     "UNPACK_COLORSPACE_CONVERSION_WEBGL",
  2388.     // gl.getBufferParameter()
  2389.     "BUFFER_USAGE",
  2390.     // gl.getFramebufferAttachmentParameter()
  2391.     "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
  2392.     // gl.getRenderbufferParameter()
  2393.     "RENDERBUFFER_INTERNAL_FORMAT",
  2394.     // gl.getTexParameter()
  2395.     "TEXTURE_MAG_FILTER",
  2396.     "TEXTURE_MIN_FILTER",
  2397.     "TEXTURE_WRAP_S",
  2398.     "TEXTURE_WRAP_T",
  2399.     // gl.getShaderParameter()
  2400.     "SHADER_TYPE",
  2401.     // gl.getVertexAttrib()
  2402.     "VERTEX_ATTRIB_ARRAY_TYPE"
  2403. ]);
  2404.  
  2405. /**
  2406.  * @const
  2407.  * @type {!Object.<string, boolean>}
  2408.  */
  2409. WebGLRenderingContextResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([
  2410.     "clear",
  2411.     "drawArrays",
  2412.     "drawElements"
  2413. ]);
  2414.  
  2415. /**
  2416.  * @param {*} obj
  2417.  * @return {?WebGLRenderingContextResource}
  2418.  */
  2419. WebGLRenderingContextResource.forObject = function(obj)
  2420. {
  2421.     var resource = Resource.forObject(obj);
  2422.     if (!resource)
  2423.         return null;
  2424.     resource = resource.contextResource();
  2425.     return (resource instanceof WebGLRenderingContextResource) ? resource : null;
  2426. }
  2427.  
  2428. WebGLRenderingContextResource.prototype = {
  2429.     /**
  2430.      * @override (overrides @return type)
  2431.      * @return {!WebGLRenderingContext}
  2432.      */
  2433.     wrappedObject: function()
  2434.     {
  2435.         return this._wrappedObject;
  2436.     },
  2437.  
  2438.     /**
  2439.      * @override
  2440.      * @return {string}
  2441.      */
  2442.     toDataURL: function()
  2443.     {
  2444.         return this.wrappedObject().canvas.toDataURL();
  2445.     },
  2446.  
  2447.     /**
  2448.      * @return {!Array.<number>}
  2449.      */
  2450.     getAllErrors: function()
  2451.     {
  2452.         var errors = [];
  2453.         var gl = this.wrappedObject();
  2454.         if (gl) {
  2455.             while (true) {
  2456.                 var error = gl.getError();
  2457.                 if (error === gl.NO_ERROR)
  2458.                     break;
  2459.                 this.clearError(error);
  2460.                 errors.push(error);
  2461.             }
  2462.         }
  2463.         if (this._customErrors) {
  2464.             for (var key in this._customErrors) {
  2465.                 var error = Number(key);
  2466.                 errors.push(error);
  2467.             }
  2468.             delete this._customErrors;
  2469.         }
  2470.         return errors;
  2471.     },
  2472.  
  2473.     /**
  2474.      * @param {!Array.<number>} errors
  2475.      */
  2476.     restoreErrors: function(errors)
  2477.     {
  2478.         var gl = this.wrappedObject();
  2479.         if (gl) {
  2480.             var wasError = false;
  2481.             while (gl.getError() !== gl.NO_ERROR)
  2482.                 wasError = true;
  2483.             console.assert(!wasError, "Error(s) while capturing current WebGL state.");
  2484.         }
  2485.         if (!errors.length)
  2486.             delete this._customErrors;
  2487.         else {
  2488.             this._customErrors = {};
  2489.             for (var i = 0, n = errors.length; i < n; ++i)
  2490.                 this._customErrors[errors[i]] = true;
  2491.         }
  2492.     },
  2493.  
  2494.     /**
  2495.      * @param {number} error
  2496.      */
  2497.     clearError: function(error)
  2498.     {
  2499.         if (this._customErrors)
  2500.             delete this._customErrors[error];
  2501.     },
  2502.  
  2503.     /**
  2504.      * @return {number}
  2505.      */
  2506.     nextError: function()
  2507.     {
  2508.         if (this._customErrors) {
  2509.             for (var key in this._customErrors) {
  2510.                 var error = Number(key);
  2511.                 delete this._customErrors[error];
  2512.                 return error;
  2513.             }
  2514.         }
  2515.         delete this._customErrors;
  2516.         var gl = this.wrappedObject();
  2517.         return gl ? gl.NO_ERROR : 0;
  2518.     },
  2519.  
  2520.     /**
  2521.      * @param {string} name
  2522.      * @param {?Object} obj
  2523.      */
  2524.     registerWebGLExtension: function(name, obj)
  2525.     {
  2526.         // FIXME: Wrap OES_vertex_array_object extension.
  2527.         var lowerName = name.toLowerCase();
  2528.         if (obj && !this._extensions[lowerName]) {
  2529.             this._extensions[lowerName] = name;
  2530.             for (var property in obj) {
  2531.                 if (TypeUtils.isEnumPropertyName(property, obj))
  2532.                     this._extensionEnums[property] = /** @type {number} */ (obj[property]);
  2533.             }
  2534.         }
  2535.     },
  2536.  
  2537.     /**
  2538.      * @param {string} name
  2539.      * @return {number|undefined}
  2540.      */
  2541.     _enumValueForName: function(name)
  2542.     {
  2543.         if (typeof this._extensionEnums[name] === "number")
  2544.             return this._extensionEnums[name];
  2545.         var gl = this.wrappedObject();
  2546.         return (typeof gl[name] === "number" ? gl[name] : undefined);
  2547.     },
  2548.  
  2549.     /**
  2550.      * @param {function(this:WebGLRenderingContext, T, number):*} func
  2551.      * @param {T} targetOrWebGLObject
  2552.      * @param {!Array.<string>} pnames
  2553.      * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output
  2554.      * @template T
  2555.      */
  2556.     queryStateValues: function(func, targetOrWebGLObject, pnames, output)
  2557.     {
  2558.         var gl = this.wrappedObject();
  2559.         for (var i = 0, pname; pname = pnames[i]; ++i) {
  2560.             var enumValue = this._enumValueForName(pname);
  2561.             if (typeof enumValue !== "number")
  2562.                 continue;
  2563.             var value = func.call(gl, targetOrWebGLObject, enumValue);
  2564.             value = Resource.forObject(value) || value;
  2565.             output.push({ name: pname, value: value, valueIsEnum: WebGLRenderingContextResource.GetResultIsEnum[pname] });
  2566.         }
  2567.     },
  2568.  
  2569.     /**
  2570.      * @override
  2571.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  2572.      */
  2573.     currentState: function()
  2574.     {
  2575.         /**
  2576.          * @param {!Object} obj
  2577.          * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output
  2578.          */
  2579.         function convertToStateDescriptors(obj, output)
  2580.         {
  2581.             for (var pname in obj)
  2582.                 output.push({ name: pname, value: obj[pname], valueIsEnum: WebGLRenderingContextResource.GetResultIsEnum[pname] });
  2583.         }
  2584.  
  2585.         var gl = this.wrappedObject();
  2586.         var glState = this._internalCurrentState(null);
  2587.  
  2588.         // VERTEX_ATTRIB_ARRAYS
  2589.         var vertexAttribStates = [];
  2590.         for (var i = 0, n = glState.VERTEX_ATTRIB_ARRAYS.length; i < n; ++i) {
  2591.             var pname = "" + i;
  2592.             var values = [];
  2593.             convertToStateDescriptors(glState.VERTEX_ATTRIB_ARRAYS[i], values);
  2594.             vertexAttribStates.push({ name: pname, values: values });
  2595.         }
  2596.         delete glState.VERTEX_ATTRIB_ARRAYS;
  2597.  
  2598.         // TEXTURE_UNITS
  2599.         var textureUnits = [];
  2600.         for (var i = 0, n = glState.TEXTURE_UNITS.length; i < n; ++i) {
  2601.             var pname = "TEXTURE" + i;
  2602.             var values = [];
  2603.             convertToStateDescriptors(glState.TEXTURE_UNITS[i], values);
  2604.             textureUnits.push({ name: pname, values: values });
  2605.         }
  2606.         delete glState.TEXTURE_UNITS;
  2607.  
  2608.         var result = [];
  2609.         convertToStateDescriptors(glState, result);
  2610.         result.push({ name: "VERTEX_ATTRIB_ARRAYS", values: vertexAttribStates, isArray: true });
  2611.         result.push({ name: "TEXTURE_UNITS", values: textureUnits, isArray: true });
  2612.  
  2613.         var textureBindingParameters = ["TEXTURE_BINDING_2D", "TEXTURE_BINDING_CUBE_MAP"];
  2614.         for (var i = 0, pname; pname = textureBindingParameters[i]; ++i) {
  2615.             var value = gl.getParameter(gl[pname]);
  2616.             value = Resource.forObject(value) || value;
  2617.             result.push({ name: pname, value: value });
  2618.         }
  2619.  
  2620.         // ENABLED_EXTENSIONS
  2621.         var enabledExtensions = [];
  2622.         for (var lowerName in this._extensions) {
  2623.             var pname = this._extensions[lowerName];
  2624.             var value = gl.getExtension(pname);
  2625.             value = Resource.forObject(value) || value;
  2626.             enabledExtensions.push({ name: pname, value: value });
  2627.         }
  2628.         result.push({ name: "ENABLED_EXTENSIONS", values: enabledExtensions, isArray: true });
  2629.  
  2630.         return result;
  2631.     },
  2632.  
  2633.     /**
  2634.      * @param {?Cache.<!ReplayableResource>} cache
  2635.      * @return {!Object.<string, *>}
  2636.      */
  2637.     _internalCurrentState: function(cache)
  2638.     {
  2639.         /**
  2640.          * @param {!Resource|*} obj
  2641.          * @return {!Resource|!ReplayableResource|*}
  2642.          */
  2643.         function maybeToReplayable(obj)
  2644.         {
  2645.             return cache ? Resource.toReplayable(obj, cache) : (Resource.forObject(obj) || obj);
  2646.         }
  2647.  
  2648.         var gl = this.wrappedObject();
  2649.         var originalErrors = this.getAllErrors();
  2650.  
  2651.         // Take a full GL state snapshot.
  2652.         var glState = Object.create(null);
  2653.         WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
  2654.             glState[parameter] = gl.isEnabled(gl[parameter]);
  2655.         });
  2656.         for (var i = 0, pname; pname = WebGLRenderingContextResource.StateParameters[i]; ++i) {
  2657.             var enumValue = this._enumValueForName(pname);
  2658.             if (typeof enumValue === "number")
  2659.                 glState[pname] = maybeToReplayable(gl.getParameter(enumValue));
  2660.         }
  2661.  
  2662.         // VERTEX_ATTRIB_ARRAYS
  2663.         var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS));
  2664.         var vertexAttribParameters = [
  2665.             "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING",
  2666.             "VERTEX_ATTRIB_ARRAY_ENABLED",
  2667.             "VERTEX_ATTRIB_ARRAY_SIZE",
  2668.             "VERTEX_ATTRIB_ARRAY_STRIDE",
  2669.             "VERTEX_ATTRIB_ARRAY_TYPE",
  2670.             "VERTEX_ATTRIB_ARRAY_NORMALIZED",
  2671.             "CURRENT_VERTEX_ATTRIB",
  2672.             "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE" // ANGLE_instanced_arrays extension
  2673.         ];
  2674.         var vertexAttribStates = [];
  2675.         for (var index = 0; index < maxVertexAttribs; ++index) {
  2676.             var state = Object.create(null);
  2677.             for (var i = 0, pname; pname = vertexAttribParameters[i]; ++i) {
  2678.                 var enumValue = this._enumValueForName(pname);
  2679.                 if (typeof enumValue === "number")
  2680.                     state[pname] = maybeToReplayable(gl.getVertexAttrib(index, enumValue));
  2681.             }
  2682.             state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(index, gl.VERTEX_ATTRIB_ARRAY_POINTER);
  2683.             vertexAttribStates.push(state);
  2684.         }
  2685.         glState.VERTEX_ATTRIB_ARRAYS = vertexAttribStates;
  2686.  
  2687.         // TEXTURE_UNITS
  2688.         var savedActiveTexture = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE));
  2689.         var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
  2690.         var textureUnits = [];
  2691.         for (var i = 0; i < maxTextureImageUnits; ++i) {
  2692.             gl.activeTexture(gl.TEXTURE0 + i);
  2693.             var state = Object.create(null);
  2694.             state.TEXTURE_2D = maybeToReplayable(gl.getParameter(gl.TEXTURE_BINDING_2D));
  2695.             state.TEXTURE_CUBE_MAP = maybeToReplayable(gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP));
  2696.             textureUnits.push(state);
  2697.         }
  2698.         glState.TEXTURE_UNITS = textureUnits;
  2699.         gl.activeTexture(savedActiveTexture);
  2700.  
  2701.         this.restoreErrors(originalErrors);
  2702.         return glState;
  2703.     },
  2704.  
  2705.     /**
  2706.      * @override
  2707.      * @param {!Object} data
  2708.      * @param {!Cache.<!ReplayableResource>} cache
  2709.      */
  2710.     _populateReplayableData: function(data, cache)
  2711.     {
  2712.         var gl = this.wrappedObject();
  2713.         data.originalCanvas = gl.canvas;
  2714.         data.originalContextAttributes = gl.getContextAttributes();
  2715.         data.extensions = TypeUtils.cloneObject(this._extensions);
  2716.         data.extensionEnums = TypeUtils.cloneObject(this._extensionEnums);
  2717.         data.glState = this._internalCurrentState(cache);
  2718.     },
  2719.  
  2720.     /**
  2721.      * @override
  2722.      * @param {!Object} data
  2723.      * @param {!Cache.<!Resource>} cache
  2724.      */
  2725.     _doReplayCalls: function(data, cache)
  2726.     {
  2727.         this._customErrors = null;
  2728.         this._extensions = TypeUtils.cloneObject(data.extensions) || {};
  2729.         this._extensionEnums = TypeUtils.cloneObject(data.extensionEnums) || {};
  2730.  
  2731.         var canvas = data.originalCanvas.cloneNode(true);
  2732.         var replayContext = null;
  2733.         var contextIds = ["experimental-webgl", "webkit-3d", "3d"];
  2734.         for (var i = 0, contextId; contextId = contextIds[i]; ++i) {
  2735.             replayContext = canvas.getContext(contextId, data.originalContextAttributes);
  2736.             if (replayContext)
  2737.                 break;
  2738.         }
  2739.  
  2740.         console.assert(replayContext, "Failed to create a WebGLRenderingContext for the replay.");
  2741.  
  2742.         var gl = /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(replayContext));
  2743.         this.setWrappedObject(gl);
  2744.  
  2745.         // Enable corresponding WebGL extensions.
  2746.         for (var name in this._extensions)
  2747.             gl.getExtension(name);
  2748.  
  2749.         var glState = data.glState;
  2750.         gl.bindFramebuffer(gl.FRAMEBUFFER, /** @type {!WebGLFramebuffer} */ (ReplayableResource.replay(glState.FRAMEBUFFER_BINDING, cache)));
  2751.         gl.bindRenderbuffer(gl.RENDERBUFFER, /** @type {!WebGLRenderbuffer} */ (ReplayableResource.replay(glState.RENDERBUFFER_BINDING, cache)));
  2752.  
  2753.         // Enable or disable server-side GL capabilities.
  2754.         WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
  2755.             console.assert(parameter in glState);
  2756.             if (glState[parameter])
  2757.                 gl.enable(gl[parameter]);
  2758.             else
  2759.                 gl.disable(gl[parameter]);
  2760.         });
  2761.  
  2762.         gl.blendColor(glState.BLEND_COLOR[0], glState.BLEND_COLOR[1], glState.BLEND_COLOR[2], glState.BLEND_COLOR[3]);
  2763.         gl.blendEquationSeparate(glState.BLEND_EQUATION_RGB, glState.BLEND_EQUATION_ALPHA);
  2764.         gl.blendFuncSeparate(glState.BLEND_SRC_RGB, glState.BLEND_DST_RGB, glState.BLEND_SRC_ALPHA, glState.BLEND_DST_ALPHA);
  2765.         gl.clearColor(glState.COLOR_CLEAR_VALUE[0], glState.COLOR_CLEAR_VALUE[1], glState.COLOR_CLEAR_VALUE[2], glState.COLOR_CLEAR_VALUE[3]);
  2766.         gl.clearDepth(glState.DEPTH_CLEAR_VALUE);
  2767.         gl.clearStencil(glState.STENCIL_CLEAR_VALUE);
  2768.         gl.colorMask(glState.COLOR_WRITEMASK[0], glState.COLOR_WRITEMASK[1], glState.COLOR_WRITEMASK[2], glState.COLOR_WRITEMASK[3]);
  2769.         gl.cullFace(glState.CULL_FACE_MODE);
  2770.         gl.depthFunc(glState.DEPTH_FUNC);
  2771.         gl.depthMask(glState.DEPTH_WRITEMASK);
  2772.         gl.depthRange(glState.DEPTH_RANGE[0], glState.DEPTH_RANGE[1]);
  2773.         gl.frontFace(glState.FRONT_FACE);
  2774.         gl.hint(gl.GENERATE_MIPMAP_HINT, glState.GENERATE_MIPMAP_HINT);
  2775.         gl.lineWidth(glState.LINE_WIDTH);
  2776.  
  2777.         var enumValue = this._enumValueForName("FRAGMENT_SHADER_DERIVATIVE_HINT_OES");
  2778.         if (typeof enumValue === "number")
  2779.             gl.hint(enumValue, glState.FRAGMENT_SHADER_DERIVATIVE_HINT_OES);
  2780.  
  2781.         WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  2782.             gl.pixelStorei(gl[parameter], glState[parameter]);
  2783.         });
  2784.  
  2785.         gl.polygonOffset(glState.POLYGON_OFFSET_FACTOR, glState.POLYGON_OFFSET_UNITS);
  2786.         gl.sampleCoverage(glState.SAMPLE_COVERAGE_VALUE, glState.SAMPLE_COVERAGE_INVERT);
  2787.         gl.stencilFuncSeparate(gl.FRONT, glState.STENCIL_FUNC, glState.STENCIL_REF, glState.STENCIL_VALUE_MASK);
  2788.         gl.stencilFuncSeparate(gl.BACK, glState.STENCIL_BACK_FUNC, glState.STENCIL_BACK_REF, glState.STENCIL_BACK_VALUE_MASK);
  2789.         gl.stencilOpSeparate(gl.FRONT, glState.STENCIL_FAIL, glState.STENCIL_PASS_DEPTH_FAIL, glState.STENCIL_PASS_DEPTH_PASS);
  2790.         gl.stencilOpSeparate(gl.BACK, glState.STENCIL_BACK_FAIL, glState.STENCIL_BACK_PASS_DEPTH_FAIL, glState.STENCIL_BACK_PASS_DEPTH_PASS);
  2791.         gl.stencilMaskSeparate(gl.FRONT, glState.STENCIL_WRITEMASK);
  2792.         gl.stencilMaskSeparate(gl.BACK, glState.STENCIL_BACK_WRITEMASK);
  2793.  
  2794.         gl.scissor(glState.SCISSOR_BOX[0], glState.SCISSOR_BOX[1], glState.SCISSOR_BOX[2], glState.SCISSOR_BOX[3]);
  2795.         gl.viewport(glState.VIEWPORT[0], glState.VIEWPORT[1], glState.VIEWPORT[2], glState.VIEWPORT[3]);
  2796.  
  2797.         gl.useProgram(/** @type {!WebGLProgram} */ (ReplayableResource.replay(glState.CURRENT_PROGRAM, cache)));
  2798.  
  2799.         // VERTEX_ATTRIB_ARRAYS
  2800.         var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS));
  2801.         for (var i = 0; i < maxVertexAttribs; ++i) {
  2802.             var state = glState.VERTEX_ATTRIB_ARRAYS[i] || {};
  2803.             if (state.VERTEX_ATTRIB_ARRAY_ENABLED)
  2804.                 gl.enableVertexAttribArray(i);
  2805.             else
  2806.                 gl.disableVertexAttribArray(i);
  2807.             if (state.CURRENT_VERTEX_ATTRIB)
  2808.                 gl.vertexAttrib4fv(i, state.CURRENT_VERTEX_ATTRIB);
  2809.             var buffer = /** @type {!WebGLBuffer} */ (ReplayableResource.replay(state.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, cache));
  2810.             if (buffer) {
  2811.                 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  2812.                 gl.vertexAttribPointer(i, state.VERTEX_ATTRIB_ARRAY_SIZE, state.VERTEX_ATTRIB_ARRAY_TYPE, state.VERTEX_ATTRIB_ARRAY_NORMALIZED, state.VERTEX_ATTRIB_ARRAY_STRIDE, state.VERTEX_ATTRIB_ARRAY_POINTER);
  2813.             }
  2814.         }
  2815.         gl.bindBuffer(gl.ARRAY_BUFFER, /** @type {!WebGLBuffer} */ (ReplayableResource.replay(glState.ARRAY_BUFFER_BINDING, cache)));
  2816.         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, /** @type {!WebGLBuffer} */ (ReplayableResource.replay(glState.ELEMENT_ARRAY_BUFFER_BINDING, cache)));
  2817.  
  2818.         // TEXTURE_UNITS
  2819.         var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
  2820.         for (var i = 0; i < maxTextureImageUnits; ++i) {
  2821.             gl.activeTexture(gl.TEXTURE0 + i);
  2822.             var state = glState.TEXTURE_UNITS[i] || {};
  2823.             gl.bindTexture(gl.TEXTURE_2D, /** @type {!WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_2D, cache)));
  2824.             gl.bindTexture(gl.TEXTURE_CUBE_MAP, /** @type {!WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_CUBE_MAP, cache)));
  2825.         }
  2826.         gl.activeTexture(glState.ACTIVE_TEXTURE);
  2827.  
  2828.         ContextResource.prototype._doReplayCalls.call(this, data, cache);
  2829.     },
  2830.  
  2831.     /**
  2832.      * @param {!Object|number} target
  2833.      * @return {?Resource}
  2834.      */
  2835.     currentBinding: function(target)
  2836.     {
  2837.         var resource = Resource.forObject(target);
  2838.         if (resource)
  2839.             return resource;
  2840.         var gl = this.wrappedObject();
  2841.         var bindingParameter;
  2842.         var bindMethodName;
  2843.         target = +target; // Explicitly convert to a number.
  2844.         var bindMethodTarget = target;
  2845.         switch (target) {
  2846.         case gl.ARRAY_BUFFER:
  2847.             bindingParameter = gl.ARRAY_BUFFER_BINDING;
  2848.             bindMethodName = "bindBuffer";
  2849.             break;
  2850.         case gl.ELEMENT_ARRAY_BUFFER:
  2851.             bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING;
  2852.             bindMethodName = "bindBuffer";
  2853.             break;
  2854.         case gl.TEXTURE_2D:
  2855.             bindingParameter = gl.TEXTURE_BINDING_2D;
  2856.             bindMethodName = "bindTexture";
  2857.             break;
  2858.         case gl.TEXTURE_CUBE_MAP:
  2859.         case gl.TEXTURE_CUBE_MAP_POSITIVE_X:
  2860.         case gl.TEXTURE_CUBE_MAP_NEGATIVE_X:
  2861.         case gl.TEXTURE_CUBE_MAP_POSITIVE_Y:
  2862.         case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y:
  2863.         case gl.TEXTURE_CUBE_MAP_POSITIVE_Z:
  2864.         case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z:
  2865.             bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP;
  2866.             bindMethodTarget = gl.TEXTURE_CUBE_MAP;
  2867.             bindMethodName = "bindTexture";
  2868.             break;
  2869.         case gl.FRAMEBUFFER:
  2870.             bindingParameter = gl.FRAMEBUFFER_BINDING;
  2871.             bindMethodName = "bindFramebuffer";
  2872.             break;
  2873.         case gl.RENDERBUFFER:
  2874.             bindingParameter = gl.RENDERBUFFER_BINDING;
  2875.             bindMethodName = "bindRenderbuffer";
  2876.             break;
  2877.         default:
  2878.             console.error("ASSERT_NOT_REACHED: unknown binding target " + target);
  2879.             return null;
  2880.         }
  2881.         resource = Resource.forObject(gl.getParameter(bindingParameter));
  2882.         if (resource)
  2883.             resource.pushBinding(bindMethodTarget, bindMethodName);
  2884.         return resource;
  2885.     },
  2886.  
  2887.     /**
  2888.      * @override
  2889.      * @param {!Call} call
  2890.      */
  2891.     onCallReplayed: function(call)
  2892.     {
  2893.         var functionName = call.functionName();
  2894.         var args = call.args();
  2895.         switch (functionName) {
  2896.         case "bindBuffer":
  2897.         case "bindFramebuffer":
  2898.         case "bindRenderbuffer":
  2899.         case "bindTexture":
  2900.             // Update BINDING state for Resources in the replay world.
  2901.             var resource = Resource.forObject(args[1]);
  2902.             if (resource)
  2903.                 resource.pushBinding(args[0], functionName);
  2904.             break;
  2905.         case "getExtension":
  2906.             this.registerWebGLExtension(args[0], /** @type {!Object} */ (call.result()));
  2907.             break;
  2908.         case "bufferData":
  2909.             var resource = /** @type {!WebGLBufferResource} */ (this.currentBinding(args[0]));
  2910.             if (resource)
  2911.                 resource.pushCall_bufferData(call);
  2912.             break;
  2913.         case "bufferSubData":
  2914.             var resource = /** @type {!WebGLBufferResource} */ (this.currentBinding(args[0]));
  2915.             if (resource)
  2916.                 resource.pushCall_bufferSubData(call);
  2917.             break;
  2918.         }
  2919.     },
  2920.  
  2921.     /**
  2922.      * @override
  2923.      * @return {!Object.<string, !Function>}
  2924.      */
  2925.     _customWrapFunctions: function()
  2926.     {
  2927.         var wrapFunctions = WebGLRenderingContextResource._wrapFunctions;
  2928.         if (!wrapFunctions) {
  2929.             wrapFunctions = Object.create(null);
  2930.  
  2931.             wrapFunctions["createBuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLBufferResource, "WebGLBuffer");
  2932.             wrapFunctions["createShader"] = Resource.WrapFunction.resourceFactoryMethod(WebGLShaderResource, "WebGLShader");
  2933.             wrapFunctions["createProgram"] = Resource.WrapFunction.resourceFactoryMethod(WebGLProgramResource, "WebGLProgram");
  2934.             wrapFunctions["createTexture"] = Resource.WrapFunction.resourceFactoryMethod(WebGLTextureResource, "WebGLTexture");
  2935.             wrapFunctions["createFramebuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLFramebufferResource, "WebGLFramebuffer");
  2936.             wrapFunctions["createRenderbuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLRenderbufferResource, "WebGLRenderbuffer");
  2937.             wrapFunctions["getUniformLocation"] = Resource.WrapFunction.resourceFactoryMethod(WebGLUniformLocationResource, "WebGLUniformLocation");
  2938.  
  2939.             stateModifyingWrapFunction("bindAttribLocation");
  2940.             stateModifyingWrapFunction("compileShader");
  2941.             stateModifyingWrapFunction("detachShader");
  2942.             stateModifyingWrapFunction("linkProgram");
  2943.             stateModifyingWrapFunction("shaderSource");
  2944.             stateModifyingWrapFunction("bufferData", WebGLBufferResource.prototype.pushCall_bufferData);
  2945.             stateModifyingWrapFunction("bufferSubData", WebGLBufferResource.prototype.pushCall_bufferSubData);
  2946.             stateModifyingWrapFunction("compressedTexImage2D");
  2947.             stateModifyingWrapFunction("compressedTexSubImage2D");
  2948.             stateModifyingWrapFunction("copyTexImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D);
  2949.             stateModifyingWrapFunction("copyTexSubImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D);
  2950.             stateModifyingWrapFunction("generateMipmap");
  2951.             stateModifyingWrapFunction("texImage2D");
  2952.             stateModifyingWrapFunction("texSubImage2D");
  2953.             stateModifyingWrapFunction("texParameterf", WebGLTextureResource.prototype.pushCall_texParameter);
  2954.             stateModifyingWrapFunction("texParameteri", WebGLTextureResource.prototype.pushCall_texParameter);
  2955.             stateModifyingWrapFunction("renderbufferStorage");
  2956.  
  2957.             /** @this {Resource.WrapFunction} */
  2958.             wrapFunctions["getError"] = function()
  2959.             {
  2960.                 var gl = /** @type {!WebGLRenderingContext} */ (this._originalObject);
  2961.                 var error = this.result();
  2962.                 if (error !== gl.NO_ERROR)
  2963.                     this._resource.clearError(error);
  2964.                 else {
  2965.                     error = this._resource.nextError();
  2966.                     if (error !== gl.NO_ERROR)
  2967.                         this.overrideResult(error);
  2968.                 }
  2969.             }
  2970.  
  2971.             /**
  2972.              * @param {string} name
  2973.              * @this {Resource.WrapFunction}
  2974.              */
  2975.             wrapFunctions["getExtension"] = function(name)
  2976.             {
  2977.                 this._resource.registerWebGLExtension(name, this.result());
  2978.             }
  2979.  
  2980.             //
  2981.             // Register bound WebGL resources.
  2982.             //
  2983.  
  2984.             /**
  2985.              * @param {!WebGLProgram} program
  2986.              * @param {!WebGLShader} shader
  2987.              * @this {Resource.WrapFunction}
  2988.              */
  2989.             wrapFunctions["attachShader"] = function(program, shader)
  2990.             {
  2991.                 var resource = this._resource.currentBinding(program);
  2992.                 if (resource) {
  2993.                     resource.pushCall(this.call());
  2994.                     var shaderResource = /** @type {!WebGLShaderResource} */ (Resource.forObject(shader));
  2995.                     if (shaderResource) {
  2996.                         var shaderType = shaderResource.type();
  2997.                         resource._registerBoundResource("__attachShader_" + shaderType, shaderResource);
  2998.                     }
  2999.                 }
  3000.             }
  3001.             /**
  3002.              * @param {number} target
  3003.              * @param {number} attachment
  3004.              * @param {number} objectTarget
  3005.              * @param {!WebGLRenderbuffer|!WebGLTexture} obj
  3006.              * @this {Resource.WrapFunction}
  3007.              */
  3008.             wrapFunctions["framebufferRenderbuffer"] = wrapFunctions["framebufferTexture2D"] = function(target, attachment, objectTarget, obj)
  3009.             {
  3010.                 var resource = this._resource.currentBinding(target);
  3011.                 if (resource) {
  3012.                     resource.pushCall(this.call());
  3013.                     resource._registerBoundResource("__framebufferAttachmentObjectName", obj);
  3014.                 }
  3015.             }
  3016.             /**
  3017.              * @param {number} target
  3018.              * @param {!Object} obj
  3019.              * @this {Resource.WrapFunction}
  3020.              */
  3021.             wrapFunctions["bindBuffer"] = wrapFunctions["bindFramebuffer"] = wrapFunctions["bindRenderbuffer"] = function(target, obj)
  3022.             {
  3023.                 this._resource.currentBinding(target); // To call WebGLBoundResource.prototype.pushBinding().
  3024.                 this._resource._registerBoundResource("__bindBuffer_" + target, obj);
  3025.             }
  3026.             /**
  3027.              * @param {number} target
  3028.              * @param {!WebGLTexture} obj
  3029.              * @this {Resource.WrapFunction}
  3030.              */
  3031.             wrapFunctions["bindTexture"] = function(target, obj)
  3032.             {
  3033.                 this._resource.currentBinding(target); // To call WebGLBoundResource.prototype.pushBinding().
  3034.                 var gl = /** @type {!WebGLRenderingContext} */ (this._originalObject);
  3035.                 var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE));
  3036.                 this._resource._registerBoundResource("__bindTexture_" + target + "_" + currentTextureBinding, obj);
  3037.             }
  3038.             /**
  3039.              * @param {!WebGLProgram} program
  3040.              * @this {Resource.WrapFunction}
  3041.              */
  3042.             wrapFunctions["useProgram"] = function(program)
  3043.             {
  3044.                 this._resource._registerBoundResource("__useProgram", program);
  3045.             }
  3046.             /**
  3047.              * @param {number} index
  3048.              * @this {Resource.WrapFunction}
  3049.              */
  3050.             wrapFunctions["vertexAttribPointer"] = function(index)
  3051.             {
  3052.                 var gl = /** @type {!WebGLRenderingContext} */ (this._originalObject);
  3053.                 this._resource._registerBoundResource("__vertexAttribPointer_" + index, gl.getParameter(gl.ARRAY_BUFFER_BINDING));
  3054.             }
  3055.  
  3056.             WebGLRenderingContextResource._wrapFunctions = wrapFunctions;
  3057.         }
  3058.  
  3059.         /**
  3060.          * @param {string} methodName
  3061.          * @param {function(this:Resource, !Call)=} pushCallFunc
  3062.          */
  3063.         function stateModifyingWrapFunction(methodName, pushCallFunc)
  3064.         {
  3065.             if (pushCallFunc) {
  3066.                 /**
  3067.                  * @param {!Object|number} target
  3068.                  * @this {Resource.WrapFunction}
  3069.                  */
  3070.                 wrapFunctions[methodName] = function(target)
  3071.                 {
  3072.                     var resource = this._resource.currentBinding(target);
  3073.                     if (resource)
  3074.                         pushCallFunc.call(resource, this.call());
  3075.                 }
  3076.             } else {
  3077.                 /**
  3078.                  * @param {!Object|number} target
  3079.                  * @this {Resource.WrapFunction}
  3080.                  */
  3081.                 wrapFunctions[methodName] = function(target)
  3082.                 {
  3083.                     var resource = this._resource.currentBinding(target);
  3084.                     if (resource)
  3085.                         resource.pushCall(this.call());
  3086.                 }
  3087.             }
  3088.         }
  3089.  
  3090.         return wrapFunctions;
  3091.     },
  3092.  
  3093.     __proto__: ContextResource.prototype
  3094. }
  3095.  
  3096. ////////////////////////////////////////////////////////////////////////////////
  3097. // 2D Canvas
  3098. ////////////////////////////////////////////////////////////////////////////////
  3099.  
  3100. /**
  3101.  * @constructor
  3102.  * @extends {ContextResource}
  3103.  * @param {!CanvasRenderingContext2D} context
  3104.  */
  3105. function CanvasRenderingContext2DResource(context)
  3106. {
  3107.     ContextResource.call(this, context, "CanvasRenderingContext2D");
  3108. }
  3109.  
  3110. /**
  3111.  * @const
  3112.  * @type {!Array.<string>}
  3113.  */
  3114. CanvasRenderingContext2DResource.AttributeProperties = [
  3115.     "strokeStyle",
  3116.     "fillStyle",
  3117.     "globalAlpha",
  3118.     "lineWidth",
  3119.     "lineCap",
  3120.     "lineJoin",
  3121.     "miterLimit",
  3122.     "shadowOffsetX",
  3123.     "shadowOffsetY",
  3124.     "shadowBlur",
  3125.     "shadowColor",
  3126.     "globalCompositeOperation",
  3127.     "font",
  3128.     "textAlign",
  3129.     "textBaseline",
  3130.     "lineDashOffset",
  3131.     "imageSmoothingEnabled",
  3132.     "webkitLineDash",
  3133.     "webkitLineDashOffset"
  3134. ];
  3135.  
  3136. /**
  3137.  * @const
  3138.  * @type {!Array.<string>}
  3139.  */
  3140. CanvasRenderingContext2DResource.PathMethods = [
  3141.     "beginPath",
  3142.     "moveTo",
  3143.     "closePath",
  3144.     "lineTo",
  3145.     "quadraticCurveTo",
  3146.     "bezierCurveTo",
  3147.     "arcTo",
  3148.     "arc",
  3149.     "rect"
  3150. ];
  3151.  
  3152. /**
  3153.  * @const
  3154.  * @type {!Array.<string>}
  3155.  */
  3156. CanvasRenderingContext2DResource.TransformationMatrixMethods = [
  3157.     "scale",
  3158.     "rotate",
  3159.     "translate",
  3160.     "transform",
  3161.     "setTransform"
  3162. ];
  3163.  
  3164. /**
  3165.  * @const
  3166.  * @type {!Object.<string, boolean>}
  3167.  */
  3168. CanvasRenderingContext2DResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([
  3169.     "clearRect",
  3170.     "drawImage",
  3171.     "drawImageFromRect",
  3172.     "drawCustomFocusRing",
  3173.     "drawFocusIfNeeded",
  3174.     "fill",
  3175.     "fillRect",
  3176.     "fillText",
  3177.     "putImageData",
  3178.     "putImageDataHD",
  3179.     "stroke",
  3180.     "strokeRect",
  3181.     "strokeText"
  3182. ]);
  3183.  
  3184. CanvasRenderingContext2DResource.prototype = {
  3185.     /**
  3186.      * @override (overrides @return type)
  3187.      * @return {!CanvasRenderingContext2D}
  3188.      */
  3189.     wrappedObject: function()
  3190.     {
  3191.         return this._wrappedObject;
  3192.     },
  3193.  
  3194.     /**
  3195.      * @override
  3196.      * @return {string}
  3197.      */
  3198.     toDataURL: function()
  3199.     {
  3200.         return this.wrappedObject().canvas.toDataURL();
  3201.     },
  3202.  
  3203.     /**
  3204.      * @override
  3205.      * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
  3206.      */
  3207.     currentState: function()
  3208.     {
  3209.         var result = [];
  3210.         var state = this._internalCurrentState(null);
  3211.         for (var pname in state)
  3212.             result.push({ name: pname, value: state[pname] });
  3213.         result.push({ name: "context", value: this.contextResource() });
  3214.         return result;
  3215.     },
  3216.  
  3217.     /**
  3218.      * @param {?Cache.<!ReplayableResource>} cache
  3219.      * @return {!Object.<string, *>}
  3220.      */
  3221.     _internalCurrentState: function(cache)
  3222.     {
  3223.         /**
  3224.          * @param {!Resource|*} obj
  3225.          * @return {!Resource|!ReplayableResource|*}
  3226.          */
  3227.         function maybeToReplayable(obj)
  3228.         {
  3229.             return cache ? Resource.toReplayable(obj, cache) : (Resource.forObject(obj) || obj);
  3230.         }
  3231.  
  3232.         var ctx = this.wrappedObject();
  3233.         var state = Object.create(null);
  3234.         CanvasRenderingContext2DResource.AttributeProperties.forEach(function(attribute) {
  3235.             if (attribute in ctx)
  3236.                 state[attribute] = maybeToReplayable(ctx[attribute]);
  3237.         });
  3238.         if (ctx.getLineDash)
  3239.             state.lineDash = ctx.getLineDash();
  3240.         return state;
  3241.     },
  3242.  
  3243.     /**
  3244.      * @param {?Object.<string, *>} state
  3245.      * @param {!Cache.<!Resource>} cache
  3246.      */
  3247.     _applyAttributesState: function(state, cache)
  3248.     {
  3249.         if (!state)
  3250.             return;
  3251.         var ctx = this.wrappedObject();
  3252.         for (var attribute in state) {
  3253.             if (attribute === "lineDash") {
  3254.                 if (ctx.setLineDash)
  3255.                     ctx.setLineDash(/** @type {!Array.<number>} */ (state[attribute]));
  3256.             } else
  3257.                 ctx[attribute] = ReplayableResource.replay(state[attribute], cache);
  3258.         }
  3259.     },
  3260.  
  3261.     /**
  3262.      * @override
  3263.      * @param {!Object} data
  3264.      * @param {!Cache.<!ReplayableResource>} cache
  3265.      */
  3266.     _populateReplayableData: function(data, cache)
  3267.     {
  3268.         var ctx = this.wrappedObject();
  3269.         // FIXME: Convert resources in the state (CanvasGradient, CanvasPattern) to Replayable.
  3270.         data.currentAttributes = this._internalCurrentState(null);
  3271.         data.originalCanvasCloned = TypeUtils.cloneIntoCanvas(/** @type {!HTMLCanvasElement} */ (ctx.canvas));
  3272.         if (ctx.getContextAttributes)
  3273.             data.originalContextAttributes = ctx.getContextAttributes();
  3274.     },
  3275.  
  3276.     /**
  3277.      * @override
  3278.      * @param {!Object} data
  3279.      * @param {!Cache.<!Resource>} cache
  3280.      */
  3281.     _doReplayCalls: function(data, cache)
  3282.     {
  3283.         var canvas = TypeUtils.cloneIntoCanvas(data.originalCanvasCloned);
  3284.         var ctx = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d", data.originalContextAttributes)));
  3285.         this.setWrappedObject(ctx);
  3286.  
  3287.         for (var i = 0, n = data.calls.length; i < n; ++i) {
  3288.             var replayableCall = /** @type {!ReplayableCall} */ (data.calls[i]);
  3289.             if (replayableCall.functionName() === "save")
  3290.                 this._applyAttributesState(replayableCall.attachment("canvas2dAttributesState"), cache);
  3291.             this._calls.push(replayableCall.replay(cache));
  3292.         }
  3293.         this._applyAttributesState(data.currentAttributes, cache);
  3294.     },
  3295.  
  3296.     /**
  3297.      * @param {!Call} call
  3298.      */
  3299.     pushCall_setTransform: function(call)
  3300.     {
  3301.         var saveCallIndex = this._lastIndexOfMatchingSaveCall();
  3302.         var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods);
  3303.         index = Math.max(index, saveCallIndex);
  3304.         if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1))
  3305.             this._removeAllObsoleteCallsFromLog();
  3306.         this.pushCall(call);
  3307.     },
  3308.  
  3309.     /**
  3310.      * @param {!Call} call
  3311.      */
  3312.     pushCall_beginPath: function(call)
  3313.     {
  3314.         var index = this._lastIndexOfAnyCall(["clip"]);
  3315.         if (this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1))
  3316.             this._removeAllObsoleteCallsFromLog();
  3317.         this.pushCall(call);
  3318.     },
  3319.  
  3320.     /**
  3321.      * @param {!Call} call
  3322.      */
  3323.     pushCall_save: function(call)
  3324.     {
  3325.         // FIXME: Convert resources in the state (CanvasGradient, CanvasPattern) to Replayable.
  3326.         call.setAttachment("canvas2dAttributesState", this._internalCurrentState(null));
  3327.         this.pushCall(call);
  3328.     },
  3329.  
  3330.     /**
  3331.      * @param {!Call} call
  3332.      */
  3333.     pushCall_restore: function(call)
  3334.     {
  3335.         var lastIndexOfSave = this._lastIndexOfMatchingSaveCall();
  3336.         if (lastIndexOfSave === -1)
  3337.             return;
  3338.         this._calls[lastIndexOfSave].setAttachment("canvas2dAttributesState", null); // No longer needed, free memory.
  3339.  
  3340.         var modified = false;
  3341.         if (this._removeCallsFromLog(["clip"], lastIndexOfSave + 1))
  3342.             modified = true;
  3343.  
  3344.         var lastIndexOfAnyPathMethod = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods);
  3345.         var index = Math.max(lastIndexOfSave, lastIndexOfAnyPathMethod);
  3346.         if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1))
  3347.             modified = true;
  3348.  
  3349.         if (modified)
  3350.             this._removeAllObsoleteCallsFromLog();
  3351.  
  3352.         var lastCall = this._calls[this._calls.length - 1];
  3353.         if (lastCall && lastCall.functionName() === "save")
  3354.             this._calls.pop();
  3355.         else
  3356.             this.pushCall(call);
  3357.     },
  3358.  
  3359.     /**
  3360.      * @param {number=} fromIndex
  3361.      * @return {number}
  3362.      */
  3363.     _lastIndexOfMatchingSaveCall: function(fromIndex)
  3364.     {
  3365.         if (typeof fromIndex !== "number")
  3366.             fromIndex = this._calls.length - 1;
  3367.         else
  3368.             fromIndex = Math.min(fromIndex, this._calls.length - 1);
  3369.         var stackDepth = 1;
  3370.         for (var i = fromIndex; i >= 0; --i) {
  3371.             var functionName = this._calls[i].functionName();
  3372.             if (functionName === "restore")
  3373.                 ++stackDepth;
  3374.             else if (functionName === "save") {
  3375.                 --stackDepth;
  3376.                 if (!stackDepth)
  3377.                     return i;
  3378.             }
  3379.         }
  3380.         return -1;
  3381.     },
  3382.  
  3383.     /**
  3384.      * @param {!Array.<string>} functionNames
  3385.      * @param {number=} fromIndex
  3386.      * @return {number}
  3387.      */
  3388.     _lastIndexOfAnyCall: function(functionNames, fromIndex)
  3389.     {
  3390.         if (typeof fromIndex !== "number")
  3391.             fromIndex = this._calls.length - 1;
  3392.         else
  3393.             fromIndex = Math.min(fromIndex, this._calls.length - 1);
  3394.         for (var i = fromIndex; i >= 0; --i) {
  3395.             if (functionNames.indexOf(this._calls[i].functionName()) !== -1)
  3396.                 return i;
  3397.         }
  3398.         return -1;
  3399.     },
  3400.  
  3401.     _removeAllObsoleteCallsFromLog: function()
  3402.     {
  3403.         // Remove all PATH methods between clip() and beginPath() calls.
  3404.         var lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"]);
  3405.         while (lastIndexOfBeginPath !== -1) {
  3406.             var index = this._lastIndexOfAnyCall(["clip"], lastIndexOfBeginPath - 1);
  3407.             this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1, lastIndexOfBeginPath);
  3408.             lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"], index - 1);
  3409.         }
  3410.  
  3411.         // Remove all TRASFORMATION MATRIX methods before restore() or setTransform() but after any PATH or corresponding save() method.
  3412.         var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"]);
  3413.         while (lastRestore !== -1) {
  3414.             var saveCallIndex = this._lastIndexOfMatchingSaveCall(lastRestore - 1);
  3415.             var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods, lastRestore - 1);
  3416.             index = Math.max(index, saveCallIndex);
  3417.             this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1, lastRestore);
  3418.             lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"], index - 1);
  3419.         }
  3420.  
  3421.         // Remove all save-restore consecutive pairs.
  3422.         var restoreCalls = 0;
  3423.         for (var i = this._calls.length - 1; i >= 0; --i) {
  3424.             var functionName = this._calls[i].functionName();
  3425.             if (functionName === "restore") {
  3426.                 ++restoreCalls;
  3427.                 continue;
  3428.             }
  3429.             if (functionName === "save" && restoreCalls > 0) {
  3430.                 var saveCallIndex = i;
  3431.                 for (var j = i - 1; j >= 0 && i - j < restoreCalls; --j) {
  3432.                     if (this._calls[j].functionName() === "save")
  3433.                         saveCallIndex = j;
  3434.                     else
  3435.                         break;
  3436.                 }
  3437.                 this._calls.splice(saveCallIndex, (i - saveCallIndex + 1) * 2);
  3438.                 i = saveCallIndex;
  3439.             }
  3440.             restoreCalls = 0;
  3441.         }
  3442.     },
  3443.  
  3444.     /**
  3445.      * @param {!Array.<string>} functionNames
  3446.      * @param {number} fromIndex
  3447.      * @param {number=} toIndex
  3448.      * @return {boolean}
  3449.      */
  3450.     _removeCallsFromLog: function(functionNames, fromIndex, toIndex)
  3451.     {
  3452.         var oldLength = this._calls.length;
  3453.         if (typeof toIndex !== "number")
  3454.             toIndex = oldLength;
  3455.         else
  3456.             toIndex = Math.min(toIndex, oldLength);
  3457.         var newIndex = Math.min(fromIndex, oldLength);
  3458.         for (var i = newIndex; i < toIndex; ++i) {
  3459.             var call = this._calls[i];
  3460.             if (functionNames.indexOf(call.functionName()) === -1)
  3461.                 this._calls[newIndex++] = call;
  3462.         }
  3463.         if (newIndex >= toIndex)
  3464.             return false;
  3465.         this._calls.splice(newIndex, toIndex - newIndex);
  3466.         return true;
  3467.     },
  3468.  
  3469.     /**
  3470.      * @override
  3471.      * @return {!Object.<string, !Function>}
  3472.      */
  3473.     _customWrapFunctions: function()
  3474.     {
  3475.         var wrapFunctions = CanvasRenderingContext2DResource._wrapFunctions;
  3476.         if (!wrapFunctions) {
  3477.             wrapFunctions = Object.create(null);
  3478.  
  3479.             wrapFunctions["createLinearGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient");
  3480.             wrapFunctions["createRadialGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient");
  3481.             wrapFunctions["createPattern"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasPattern");
  3482.  
  3483.             for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.TransformationMatrixMethods[i]; ++i)
  3484.                 stateModifyingWrapFunction(methodName, methodName === "setTransform" ? this.pushCall_setTransform : undefined);
  3485.             for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.PathMethods[i]; ++i)
  3486.                 stateModifyingWrapFunction(methodName, methodName === "beginPath" ? this.pushCall_beginPath : undefined);
  3487.  
  3488.             stateModifyingWrapFunction("save", this.pushCall_save);
  3489.             stateModifyingWrapFunction("restore", this.pushCall_restore);
  3490.             stateModifyingWrapFunction("clip");
  3491.  
  3492.             CanvasRenderingContext2DResource._wrapFunctions = wrapFunctions;
  3493.         }
  3494.  
  3495.         /**
  3496.          * @param {string} methodName
  3497.          * @param {function(this:Resource, !Call)=} func
  3498.          */
  3499.         function stateModifyingWrapFunction(methodName, func)
  3500.         {
  3501.             if (func) {
  3502.                 /** @this {Resource.WrapFunction} */
  3503.                 wrapFunctions[methodName] = function()
  3504.                 {
  3505.                     func.call(this._resource, this.call());
  3506.                 }
  3507.             } else {
  3508.                 /** @this {Resource.WrapFunction} */
  3509.                 wrapFunctions[methodName] = function()
  3510.                 {
  3511.                     this._resource.pushCall(this.call());
  3512.                 }
  3513.             }
  3514.         }
  3515.  
  3516.         return wrapFunctions;
  3517.     },
  3518.  
  3519.     __proto__: ContextResource.prototype
  3520. }
  3521.  
  3522. /**
  3523.  * @constructor
  3524.  * @param {!Object.<string, boolean>=} drawingMethodNames
  3525.  */
  3526. function CallFormatter(drawingMethodNames)
  3527. {
  3528.     this._drawingMethodNames = drawingMethodNames || Object.create(null);
  3529. }
  3530.  
  3531. CallFormatter.prototype = {
  3532.     /**
  3533.      * @param {!ReplayableCall} replayableCall
  3534.      * @param {string=} objectGroup
  3535.      * @return {!Object}
  3536.      */
  3537.     formatCall: function(replayableCall, objectGroup)
  3538.     {
  3539.         var result = {};
  3540.         var functionName = replayableCall.functionName();
  3541.         if (functionName) {
  3542.             result.functionName = functionName;
  3543.             result.arguments = [];
  3544.             var args = replayableCall.args();
  3545.             for (var i = 0, n = args.length; i < n; ++i)
  3546.                 result.arguments.push(this.formatValue(args[i], objectGroup));
  3547.             if (replayableCall.result() !== undefined)
  3548.                 result.result = this.formatValue(replayableCall.result(), objectGroup);
  3549.             if (this._drawingMethodNames[functionName])
  3550.                 result.isDrawingCall = true;
  3551.         } else {
  3552.             result.property = replayableCall.propertyName();
  3553.             result.value = this.formatValue(replayableCall.propertyValue(), objectGroup);
  3554.         }
  3555.         return result;
  3556.     },
  3557.  
  3558.     /**
  3559.      * @param {*} value
  3560.      * @param {string=} objectGroup
  3561.      * @return {!CanvasAgent.CallArgument}
  3562.      */
  3563.     formatValue: function(value, objectGroup)
  3564.     {
  3565.         if (value instanceof Resource || value instanceof ReplayableResource) {
  3566.             return {
  3567.                 description: value.description(),
  3568.                 resourceId: CallFormatter.makeStringResourceId(value.id())
  3569.             };
  3570.         }
  3571.  
  3572.         var remoteObject = injectedScript.wrapObject(value, objectGroup || "", true, false);
  3573.         var description = remoteObject.description || ("" + value);
  3574.  
  3575.         var result = {
  3576.             description: description,
  3577.             type: /** @type {!CanvasAgent.CallArgumentType} */ (remoteObject.type)
  3578.         };
  3579.         if (remoteObject.subtype)
  3580.             result.subtype = /** @type {!CanvasAgent.CallArgumentSubtype} */ (remoteObject.subtype);
  3581.         if (remoteObject.objectId) {
  3582.             if (objectGroup)
  3583.                 result.remoteObject = remoteObject;
  3584.             else
  3585.                 injectedScript.releaseObject(remoteObject.objectId);
  3586.         }
  3587.         return result;
  3588.     },
  3589.  
  3590.     /**
  3591.      * @param {string} name
  3592.      * @return {?string}
  3593.      */
  3594.     enumValueForName: function(name)
  3595.     {
  3596.         return null;
  3597.     },
  3598.  
  3599.     /**
  3600.      * @param {number} value
  3601.      * @param {!Array.<string>=} options
  3602.      * @return {?string}
  3603.      */
  3604.     enumNameForValue: function(value, options)
  3605.     {
  3606.         return null;
  3607.     },
  3608.  
  3609.     /**
  3610.      * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} descriptors
  3611.      * @param {string=} objectGroup
  3612.      * @return {!Array.<!CanvasAgent.ResourceStateDescriptor>}
  3613.      */
  3614.     formatResourceStateDescriptors: function(descriptors, objectGroup)
  3615.     {
  3616.         var result = [];
  3617.         for (var i = 0, n = descriptors.length; i < n; ++i) {
  3618.             var d = descriptors[i];
  3619.             var item;
  3620.             if (d.values)
  3621.                 item = { name: d.name, values: this.formatResourceStateDescriptors(d.values, objectGroup) };
  3622.             else {
  3623.                 item = { name: d.name, value: this.formatValue(d.value, objectGroup) };
  3624.                 if (d.valueIsEnum && typeof d.value === "number") {
  3625.                     var enumName = this.enumNameForValue(d.value);
  3626.                     if (enumName)
  3627.                         item.value.enumName = enumName;
  3628.                 }
  3629.             }
  3630.             var enumValue = this.enumValueForName(d.name);
  3631.             if (enumValue)
  3632.                 item.enumValueForName = enumValue;
  3633.             if (d.isArray)
  3634.                 item.isArray = true;
  3635.             result.push(item);
  3636.         }
  3637.         return result;
  3638.     }
  3639. }
  3640.  
  3641. /**
  3642.  * @const
  3643.  * @type {!Object.<string, !CallFormatter>}
  3644.  */
  3645. CallFormatter._formatters = {};
  3646.  
  3647. /**
  3648.  * @param {string} resourceName
  3649.  * @param {!CallFormatter} callFormatter
  3650.  */
  3651. CallFormatter.register = function(resourceName, callFormatter)
  3652. {
  3653.     CallFormatter._formatters[resourceName] = callFormatter;
  3654. }
  3655.  
  3656. /**
  3657.  * @param {!Resource|!ReplayableResource} resource
  3658.  * @return {!CallFormatter}
  3659.  */
  3660. CallFormatter.forResource = function(resource)
  3661. {
  3662.     var formatter = CallFormatter._formatters[resource.name()];
  3663.     if (!formatter) {
  3664.         var contextResource = resource.contextResource();
  3665.         formatter = (contextResource && CallFormatter._formatters[contextResource.name()]) || new CallFormatter();
  3666.     }
  3667.     return formatter;
  3668. }
  3669.  
  3670. /**
  3671.  * @param {number} resourceId
  3672.  * @return {!CanvasAgent.ResourceId}
  3673.  */
  3674. CallFormatter.makeStringResourceId = function(resourceId)
  3675. {
  3676.     return "{\"injectedScriptId\":" + injectedScriptId + ",\"resourceId\":" + resourceId + "}";
  3677. }
  3678.  
  3679. /**
  3680.  * @constructor
  3681.  * @extends {CallFormatter}
  3682.  * @param {!Object.<string, boolean>} drawingMethodNames
  3683.  */
  3684. function WebGLCallFormatter(drawingMethodNames)
  3685. {
  3686.     CallFormatter.call(this, drawingMethodNames);
  3687. }
  3688.  
  3689. /**
  3690.  * NOTE: The code below is generated from the IDL file by the script:
  3691.  * /devtools/scripts/check_injected_webgl_calls_info.py
  3692.  *
  3693.  * @type {!Array.<{aname: string, enum: (!Array.<number>|undefined), bitfield: (!Array.<number>|undefined), returnType: string, hints: (!Array.<string>|undefined)}>}
  3694.  */
  3695. WebGLCallFormatter.EnumsInfo = [
  3696.     {"aname": "activeTexture", "enum": [0]},
  3697.     {"aname": "bindBuffer", "enum": [0]},
  3698.     {"aname": "bindFramebuffer", "enum": [0]},
  3699.     {"aname": "bindRenderbuffer", "enum": [0]},
  3700.     {"aname": "bindTexture", "enum": [0]},
  3701.     {"aname": "blendEquation", "enum": [0]},
  3702.     {"aname": "blendEquationSeparate", "enum": [0, 1]},
  3703.     {"aname": "blendFunc", "enum": [0, 1], "hints": ["ZERO", "ONE"]},
  3704.     {"aname": "blendFuncSeparate", "enum": [0, 1, 2, 3], "hints": ["ZERO", "ONE"]},
  3705.     {"aname": "bufferData", "enum": [0, 2]},
  3706.     {"aname": "bufferSubData", "enum": [0]},
  3707.     {"aname": "checkFramebufferStatus", "enum": [0], "returnType": "enum"},
  3708.     {"aname": "clear", "bitfield": [0]},
  3709.     {"aname": "compressedTexImage2D", "enum": [0, 2]},
  3710.     {"aname": "compressedTexSubImage2D", "enum": [0, 6]},
  3711.     {"aname": "copyTexImage2D", "enum": [0, 2]},
  3712.     {"aname": "copyTexSubImage2D", "enum": [0]},
  3713.     {"aname": "createShader", "enum": [0]},
  3714.     {"aname": "cullFace", "enum": [0]},
  3715.     {"aname": "depthFunc", "enum": [0]},
  3716.     {"aname": "disable", "enum": [0]},
  3717.     {"aname": "drawArrays", "enum": [0], "hints": ["POINTS", "LINES"]},
  3718.     {"aname": "drawElements", "enum": [0, 2], "hints": ["POINTS", "LINES"]},
  3719.     {"aname": "enable", "enum": [0]},
  3720.     {"aname": "framebufferRenderbuffer", "enum": [0, 1, 2]},
  3721.     {"aname": "framebufferTexture2D", "enum": [0, 1, 2]},
  3722.     {"aname": "frontFace", "enum": [0]},
  3723.     {"aname": "generateMipmap", "enum": [0]},
  3724.     {"aname": "getBufferParameter", "enum": [0, 1]},
  3725.     {"aname": "getError", "hints": ["NO_ERROR"], "returnType": "enum"},
  3726.     {"aname": "getFramebufferAttachmentParameter", "enum": [0, 1, 2]},
  3727.     {"aname": "getParameter", "enum": [0]},
  3728.     {"aname": "getProgramParameter", "enum": [1]},
  3729.     {"aname": "getRenderbufferParameter", "enum": [0, 1]},
  3730.     {"aname": "getShaderParameter", "enum": [1]},
  3731.     {"aname": "getShaderPrecisionFormat", "enum": [0, 1]},
  3732.     {"aname": "getTexParameter", "enum": [0, 1], "returnType": "enum"},
  3733.     {"aname": "getVertexAttrib", "enum": [1]},
  3734.     {"aname": "getVertexAttribOffset", "enum": [1]},
  3735.     {"aname": "hint", "enum": [0, 1]},
  3736.     {"aname": "isEnabled", "enum": [0]},
  3737.     {"aname": "pixelStorei", "enum": [0]},
  3738.     {"aname": "readPixels", "enum": [4, 5]},
  3739.     {"aname": "renderbufferStorage", "enum": [0, 1]},
  3740.     {"aname": "stencilFunc", "enum": [0]},
  3741.     {"aname": "stencilFuncSeparate", "enum": [0, 1]},
  3742.     {"aname": "stencilMaskSeparate", "enum": [0]},
  3743.     {"aname": "stencilOp", "enum": [0, 1, 2], "hints": ["ZERO", "ONE"]},
  3744.     {"aname": "stencilOpSeparate", "enum": [0, 1, 2, 3], "hints": ["ZERO", "ONE"]},
  3745.     {"aname": "texParameterf", "enum": [0, 1, 2]},
  3746.     {"aname": "texParameteri", "enum": [0, 1, 2]},
  3747.     {"aname": "texImage2D", "enum": [0, 2, 6, 7]},
  3748.     {"aname": "texImage2D", "enum": [0, 2, 3, 4]},
  3749.     {"aname": "texSubImage2D", "enum": [0, 6, 7]},
  3750.     {"aname": "texSubImage2D", "enum": [0, 4, 5]},
  3751.     {"aname": "vertexAttribPointer", "enum": [2]}
  3752. ];
  3753.  
  3754. WebGLCallFormatter.prototype = {
  3755.     /**
  3756.      * @override
  3757.      * @param {!ReplayableCall} replayableCall
  3758.      * @param {string=} objectGroup
  3759.      * @return {!Object}
  3760.      */
  3761.     formatCall: function(replayableCall, objectGroup)
  3762.     {
  3763.         var result = CallFormatter.prototype.formatCall.call(this, replayableCall, objectGroup);
  3764.         if (!result.functionName)
  3765.             return result;
  3766.         var enumsInfo = this._findEnumsInfo(replayableCall);
  3767.         if (!enumsInfo)
  3768.             return result;
  3769.         var enumArgsIndexes = enumsInfo["enum"] || [];
  3770.         for (var i = 0, n = enumArgsIndexes.length; i < n; ++i) {
  3771.             var index = enumArgsIndexes[i];
  3772.             var callArgument = result.arguments[index];
  3773.             this._formatEnumValue(callArgument, enumsInfo["hints"]);
  3774.         }
  3775.         var bitfieldArgsIndexes = enumsInfo["bitfield"] || [];
  3776.         for (var i = 0, n = bitfieldArgsIndexes.length; i < n; ++i) {
  3777.             var index = bitfieldArgsIndexes[i];
  3778.             var callArgument = result.arguments[index];
  3779.             this._formatEnumBitmaskValue(callArgument, enumsInfo["hints"]);
  3780.         }
  3781.         if (enumsInfo.returnType === "enum")
  3782.             this._formatEnumValue(result.result, enumsInfo["hints"]);
  3783.         else if (enumsInfo.returnType === "bitfield")
  3784.             this._formatEnumBitmaskValue(result.result, enumsInfo["hints"]);
  3785.         return result;
  3786.     },
  3787.  
  3788.     /**
  3789.      * @override
  3790.      * @param {string} name
  3791.      * @return {?string}
  3792.      */
  3793.     enumValueForName: function(name)
  3794.     {
  3795.         this._initialize();
  3796.         if (name in this._enumNameToValue)
  3797.             return "" + this._enumNameToValue[name];
  3798.         return null;
  3799.     },
  3800.  
  3801.     /**
  3802.      * @override
  3803.      * @param {number} value
  3804.      * @param {!Array.<string>=} options
  3805.      * @return {?string}
  3806.      */
  3807.     enumNameForValue: function(value, options)
  3808.     {
  3809.         this._initialize();
  3810.         options = options || [];
  3811.         for (var i = 0, n = options.length; i < n; ++i) {
  3812.             if (this._enumNameToValue[options[i]] === value)
  3813.                 return options[i];
  3814.         }
  3815.         var names = this._enumValueToNames[value];
  3816.         if (!names || names.length !== 1)
  3817.             return null;
  3818.         return names[0];
  3819.     },
  3820.  
  3821.     /**
  3822.      * @param {!ReplayableCall} replayableCall
  3823.      * @return {?Object}
  3824.      */
  3825.     _findEnumsInfo: function(replayableCall)
  3826.     {
  3827.         function findMaxArgumentIndex(enumsInfo)
  3828.         {
  3829.             var result = -1;
  3830.             var enumArgsIndexes = enumsInfo["enum"] || [];
  3831.             for (var i = 0, n = enumArgsIndexes.length; i < n; ++i)
  3832.                 result = Math.max(result, enumArgsIndexes[i]);
  3833.             var bitfieldArgsIndexes = enumsInfo["bitfield"] || [];
  3834.             for (var i = 0, n = bitfieldArgsIndexes.length; i < n; ++i)
  3835.                 result = Math.max(result, bitfieldArgsIndexes[i]);
  3836.             return result;
  3837.         }
  3838.  
  3839.         var result = null;
  3840.         for (var i = 0, enumsInfo; enumsInfo = WebGLCallFormatter.EnumsInfo[i]; ++i) {
  3841.             if (enumsInfo["aname"] !== replayableCall.functionName())
  3842.                 continue;
  3843.             var argsCount = replayableCall.args().length;
  3844.             var maxArgumentIndex = findMaxArgumentIndex(enumsInfo);
  3845.             if (maxArgumentIndex >= argsCount)
  3846.                 continue;
  3847.             // To resolve ambiguity (see texImage2D, texSubImage2D) choose description with max argument indexes.
  3848.             if (!result || findMaxArgumentIndex(result) < maxArgumentIndex)
  3849.                 result = enumsInfo;
  3850.         }
  3851.         return result;
  3852.     },
  3853.  
  3854.     /**
  3855.      * @param {?CanvasAgent.CallArgument|undefined} callArgument
  3856.      * @param {!Array.<string>=} options
  3857.      */
  3858.     _formatEnumValue: function(callArgument, options)
  3859.     {
  3860.         if (!callArgument || isNaN(callArgument.description))
  3861.             return;
  3862.         this._initialize();
  3863.         var value = +callArgument.description;
  3864.         var enumName = this.enumNameForValue(value, options);
  3865.         if (enumName)
  3866.             callArgument.enumName = enumName;
  3867.     },
  3868.  
  3869.     /**
  3870.      * @param {?CanvasAgent.CallArgument|undefined} callArgument
  3871.      * @param {!Array.<string>=} options
  3872.      */
  3873.     _formatEnumBitmaskValue: function(callArgument, options)
  3874.     {
  3875.         if (!callArgument || isNaN(callArgument.description))
  3876.             return;
  3877.         this._initialize();
  3878.         var value = +callArgument.description;
  3879.         options = options || [];
  3880.         /** @type {!Array.<string>} */
  3881.         var result = [];
  3882.         for (var i = 0, n = options.length; i < n; ++i) {
  3883.             var bitValue = this._enumNameToValue[options[i]] || 0;
  3884.             if (value & bitValue) {
  3885.                 result.push(options[i]);
  3886.                 value &= ~bitValue;
  3887.             }
  3888.         }
  3889.         while (value) {
  3890.             var nextValue = value & (value - 1);
  3891.             var bitValue = value ^ nextValue;
  3892.             var names = this._enumValueToNames[bitValue];
  3893.             if (!names || names.length !== 1) {
  3894.                 console.warn("Ambiguous WebGL enum names for value " + bitValue + ": " + names);
  3895.                 return;
  3896.             }
  3897.             result.push(names[0]);
  3898.             value = nextValue;
  3899.         }
  3900.         result.sort();
  3901.         callArgument.enumName = result.join(" | ");
  3902.     },
  3903.  
  3904.     _initialize: function()
  3905.     {
  3906.         if (this._enumNameToValue)
  3907.             return;
  3908.  
  3909.         /** @type {!Object.<string, number>} */
  3910.         this._enumNameToValue = Object.create(null);
  3911.         /** @type {!Object.<number, !Array.<string>>} */
  3912.         this._enumValueToNames = Object.create(null);
  3913.  
  3914.         /**
  3915.          * @param {?Object} obj
  3916.          * @this WebGLCallFormatter
  3917.          */
  3918.         function iterateWebGLEnums(obj)
  3919.         {
  3920.             if (!obj)
  3921.                 return;
  3922.             for (var property in obj) {
  3923.                 if (TypeUtils.isEnumPropertyName(property, obj)) {
  3924.                     var value = /** @type {number} */ (obj[property]);
  3925.                     this._enumNameToValue[property] = value;
  3926.                     var names = this._enumValueToNames[value];
  3927.                     if (names) {
  3928.                         if (names.indexOf(property) === -1)
  3929.                             names.push(property);
  3930.                     } else
  3931.                         this._enumValueToNames[value] = [property];
  3932.                 }
  3933.             }
  3934.         }
  3935.  
  3936.         /**
  3937.          * @param {!Array.<string>} values
  3938.          * @return {string}
  3939.          */
  3940.         function commonSubstring(values)
  3941.         {
  3942.             var length = values.length;
  3943.             for (var i = 0; i < length; ++i) {
  3944.                 for (var j = 0; j < length; ++j) {
  3945.                     if (values[j].indexOf(values[i]) === -1)
  3946.                         break;
  3947.                 }
  3948.                 if (j === length)
  3949.                     return values[i];
  3950.             }
  3951.             return "";
  3952.         }
  3953.  
  3954.         var gl = this._createUninstrumentedWebGLRenderingContext();
  3955.         iterateWebGLEnums.call(this, gl);
  3956.  
  3957.         var extensions = gl.getSupportedExtensions() || [];
  3958.         for (var i = 0, n = extensions.length; i < n; ++i)
  3959.             iterateWebGLEnums.call(this, gl.getExtension(extensions[i]));
  3960.  
  3961.         // Sort to get rid of ambiguity.
  3962.         for (var value in this._enumValueToNames) {
  3963.             var numericValue = Number(value);
  3964.             var names = this._enumValueToNames[numericValue];
  3965.             if (names.length > 1) {
  3966.                 // Choose one enum name if possible. For example:
  3967.                 //   [BLEND_EQUATION, BLEND_EQUATION_RGB] => BLEND_EQUATION
  3968.                 //   [COLOR_ATTACHMENT0, COLOR_ATTACHMENT0_WEBGL] => COLOR_ATTACHMENT0
  3969.                 var common = commonSubstring(names);
  3970.                 if (common)
  3971.                     this._enumValueToNames[numericValue] = [common];
  3972.                 else
  3973.                     this._enumValueToNames[numericValue] = names.sort();
  3974.             }
  3975.         }
  3976.     },
  3977.  
  3978.     /**
  3979.      * @return {?WebGLRenderingContext}
  3980.      */
  3981.     _createUninstrumentedWebGLRenderingContext: function()
  3982.     {
  3983.         var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas"));
  3984.         var contextIds = ["experimental-webgl", "webkit-3d", "3d"];
  3985.         for (var i = 0, contextId; contextId = contextIds[i]; ++i) {
  3986.             var context = canvas.getContext(contextId);
  3987.             if (context)
  3988.                 return /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(context));
  3989.         }
  3990.         return null;
  3991.     },
  3992.  
  3993.     __proto__: CallFormatter.prototype
  3994. }
  3995.  
  3996. CallFormatter.register("CanvasRenderingContext2D", new CallFormatter(CanvasRenderingContext2DResource.DrawingMethods));
  3997. CallFormatter.register("WebGLRenderingContext", new WebGLCallFormatter(WebGLRenderingContextResource.DrawingMethods));
  3998.  
  3999. /**
  4000.  * @constructor
  4001.  */
  4002. function TraceLog()
  4003. {
  4004.     /** @type {!Array.<!ReplayableCall>} */
  4005.     this._replayableCalls = [];
  4006.     /** @type {!Cache.<!ReplayableResource>} */
  4007.     this._replayablesCache = new Cache();
  4008.     /** @type {!Object.<number, boolean>} */
  4009.     this._frameEndCallIndexes = {};
  4010.     /** @type {!Object.<number, boolean>} */
  4011.     this._resourcesCreatedInThisTraceLog = {};
  4012. }
  4013.  
  4014. TraceLog.prototype = {
  4015.     /**
  4016.      * @return {number}
  4017.      */
  4018.     size: function()
  4019.     {
  4020.         return this._replayableCalls.length;
  4021.     },
  4022.  
  4023.     /**
  4024.      * @return {!Array.<!ReplayableCall>}
  4025.      */
  4026.     replayableCalls: function()
  4027.     {
  4028.         return this._replayableCalls;
  4029.     },
  4030.  
  4031.     /**
  4032.      * @param {number} id
  4033.      * @return {!ReplayableResource|undefined}
  4034.      */
  4035.     replayableResource: function(id)
  4036.     {
  4037.         return this._replayablesCache.get(id);
  4038.     },
  4039.  
  4040.     /**
  4041.      * @param {number} resourceId
  4042.      * @return {boolean}
  4043.      */
  4044.     createdInThisTraceLog: function(resourceId)
  4045.     {
  4046.         return !!this._resourcesCreatedInThisTraceLog[resourceId];
  4047.     },
  4048.  
  4049.     /**
  4050.      * @param {!Resource} resource
  4051.      */
  4052.     captureResource: function(resource)
  4053.     {
  4054.         resource.toReplayable(this._replayablesCache);
  4055.     },
  4056.  
  4057.     /**
  4058.      * @param {!Call} call
  4059.      */
  4060.     addCall: function(call)
  4061.     {
  4062.         var resource = Resource.forObject(call.result());
  4063.         if (resource && !this._replayablesCache.has(resource.id()))
  4064.             this._resourcesCreatedInThisTraceLog[resource.id()] = true;
  4065.         this._replayableCalls.push(call.toReplayable(this._replayablesCache));
  4066.     },
  4067.  
  4068.     addFrameEndMark: function()
  4069.     {
  4070.         var index = this._replayableCalls.length - 1;
  4071.         if (index >= 0)
  4072.             this._frameEndCallIndexes[index] = true;
  4073.     },
  4074.  
  4075.     /**
  4076.      * @param {number} index
  4077.      * @return {boolean}
  4078.      */
  4079.     isFrameEndCallAt: function(index)
  4080.     {
  4081.         return !!this._frameEndCallIndexes[index];
  4082.     }
  4083. }
  4084.  
  4085. /**
  4086.  * @constructor
  4087.  * @param {!TraceLog} traceLog
  4088.  */
  4089. function TraceLogPlayer(traceLog)
  4090. {
  4091.     /** @type {!TraceLog} */
  4092.     this._traceLog = traceLog;
  4093.     /** @type {number} */
  4094.     this._nextReplayStep = 0;
  4095.     /** @type {!Cache.<!Resource>} */
  4096.     this._replayWorldCache = new Cache();
  4097. }
  4098.  
  4099. TraceLogPlayer.prototype = {
  4100.     /**
  4101.      * @return {!TraceLog}
  4102.      */
  4103.     traceLog: function()
  4104.     {
  4105.         return this._traceLog;
  4106.     },
  4107.  
  4108.     /**
  4109.      * @param {number} id
  4110.      * @return {!Resource|undefined}
  4111.      */
  4112.     replayWorldResource: function(id)
  4113.     {
  4114.         return this._replayWorldCache.get(id);
  4115.     },
  4116.  
  4117.     /**
  4118.      * @return {number}
  4119.      */
  4120.     nextReplayStep: function()
  4121.     {
  4122.         return this._nextReplayStep;
  4123.     },
  4124.  
  4125.     reset: function()
  4126.     {
  4127.         this._nextReplayStep = 0;
  4128.         this._replayWorldCache.reset();
  4129.     },
  4130.  
  4131.     /**
  4132.      * @param {number} stepNum
  4133.      * @return {{replayTime:number, lastCall:(!Call)}}
  4134.      */
  4135.     stepTo: function(stepNum)
  4136.     {
  4137.         stepNum = Math.min(stepNum, this._traceLog.size() - 1);
  4138.         console.assert(stepNum >= 0);
  4139.         if (this._nextReplayStep > stepNum)
  4140.             this.reset();
  4141.  
  4142.         // Replay the calls' arguments first to warm-up, before measuring the actual replay time.
  4143.         this._replayCallArguments(stepNum);
  4144.  
  4145.         var replayableCalls = this._traceLog.replayableCalls();
  4146.         var replayedCalls = [];
  4147.         replayedCalls.length = stepNum - this._nextReplayStep + 1;
  4148.  
  4149.         var beforeTime = TypeUtils.now();
  4150.         for (var i = 0; this._nextReplayStep <= stepNum; ++this._nextReplayStep, ++i)
  4151.             replayedCalls[i] = replayableCalls[this._nextReplayStep].replay(this._replayWorldCache);
  4152.         var replayTime = Math.max(0, TypeUtils.now() - beforeTime);
  4153.  
  4154.         for (var i = 0, call; call = replayedCalls[i]; ++i)
  4155.             call.resource().onCallReplayed(call);
  4156.  
  4157.         return {
  4158.             replayTime: replayTime,
  4159.             lastCall: replayedCalls[replayedCalls.length - 1]
  4160.         };
  4161.     },
  4162.  
  4163.     /**
  4164.      * @param {number} stepNum
  4165.      */
  4166.     _replayCallArguments: function(stepNum)
  4167.     {
  4168.         /**
  4169.          * @param {*} obj
  4170.          * @this {TraceLogPlayer}
  4171.          */
  4172.         function replayIfNotCreatedInThisTraceLog(obj)
  4173.         {
  4174.             if (!(obj instanceof ReplayableResource))
  4175.                 return;
  4176.             var replayableResource = /** @type {!ReplayableResource} */ (obj);
  4177.             if (!this._traceLog.createdInThisTraceLog(replayableResource.id()))
  4178.                 replayableResource.replay(this._replayWorldCache)
  4179.         }
  4180.         var replayableCalls = this._traceLog.replayableCalls();
  4181.         for (var i = this._nextReplayStep; i <= stepNum; ++i) {
  4182.             replayIfNotCreatedInThisTraceLog.call(this, replayableCalls[i].replayableResource());
  4183.             replayIfNotCreatedInThisTraceLog.call(this, replayableCalls[i].result());
  4184.             replayableCalls[i].args().forEach(replayIfNotCreatedInThisTraceLog.bind(this));
  4185.         }
  4186.     }
  4187. }
  4188.  
  4189. /**
  4190.  * @constructor
  4191.  */
  4192. function ResourceTrackingManager()
  4193. {
  4194.     this._capturing = false;
  4195.     this._stopCapturingOnFrameEnd = false;
  4196.     this._lastTraceLog = null;
  4197. }
  4198.  
  4199. ResourceTrackingManager.prototype = {
  4200.     /**
  4201.      * @return {boolean}
  4202.      */
  4203.     capturing: function()
  4204.     {
  4205.         return this._capturing;
  4206.     },
  4207.  
  4208.     /**
  4209.      * @return {?TraceLog}
  4210.      */
  4211.     lastTraceLog: function()
  4212.     {
  4213.         return this._lastTraceLog;
  4214.     },
  4215.  
  4216.     /**
  4217.      * @param {!Resource} resource
  4218.      */
  4219.     registerResource: function(resource)
  4220.     {
  4221.         resource.setManager(this);
  4222.     },
  4223.  
  4224.     startCapturing: function()
  4225.     {
  4226.         if (!this._capturing)
  4227.             this._lastTraceLog = new TraceLog();
  4228.         this._capturing = true;
  4229.         this._stopCapturingOnFrameEnd = false;
  4230.     },
  4231.  
  4232.     /**
  4233.      * @param {!TraceLog=} traceLog
  4234.      */
  4235.     stopCapturing: function(traceLog)
  4236.     {
  4237.         if (traceLog && this._lastTraceLog !== traceLog)
  4238.             return;
  4239.         this._capturing = false;
  4240.         this._stopCapturingOnFrameEnd = false;
  4241.         if (this._lastTraceLog)
  4242.             this._lastTraceLog.addFrameEndMark();
  4243.     },
  4244.  
  4245.     /**
  4246.      * @param {!TraceLog} traceLog
  4247.      */
  4248.     dropTraceLog: function(traceLog)
  4249.     {
  4250.         this.stopCapturing(traceLog);
  4251.         if (this._lastTraceLog === traceLog)
  4252.             this._lastTraceLog = null;
  4253.     },
  4254.  
  4255.     captureFrame: function()
  4256.     {
  4257.         this._lastTraceLog = new TraceLog();
  4258.         this._capturing = true;
  4259.         this._stopCapturingOnFrameEnd = true;
  4260.     },
  4261.  
  4262.     /**
  4263.      * @param {!Resource} resource
  4264.      * @param {!Array|!Arguments} args
  4265.      */
  4266.     captureArguments: function(resource, args)
  4267.     {
  4268.         if (!this._capturing)
  4269.             return;
  4270.         this._lastTraceLog.captureResource(resource);
  4271.         for (var i = 0, n = args.length; i < n; ++i) {
  4272.             var res = Resource.forObject(args[i]);
  4273.             if (res)
  4274.                 this._lastTraceLog.captureResource(res);
  4275.         }
  4276.     },
  4277.  
  4278.     /**
  4279.      * @param {!Call} call
  4280.      */
  4281.     captureCall: function(call)
  4282.     {
  4283.         if (!this._capturing)
  4284.             return;
  4285.         this._lastTraceLog.addCall(call);
  4286.     },
  4287.  
  4288.     markFrameEnd: function()
  4289.     {
  4290.         if (!this._lastTraceLog)
  4291.             return;
  4292.         this._lastTraceLog.addFrameEndMark();
  4293.         if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size())
  4294.             this.stopCapturing(this._lastTraceLog);
  4295.     }
  4296. }
  4297.  
  4298. /**
  4299.  * @constructor
  4300.  */
  4301. var InjectedCanvasModule = function()
  4302. {
  4303.     /** @type {!ResourceTrackingManager} */
  4304.     this._manager = new ResourceTrackingManager();
  4305.     /** @type {number} */
  4306.     this._lastTraceLogId = 0;
  4307.     /** @type {!Object.<string, !TraceLog>} */
  4308.     this._traceLogs = {};
  4309.     /** @type {!Object.<string, !TraceLogPlayer>} */
  4310.     this._traceLogPlayers = {};
  4311. }
  4312.  
  4313. InjectedCanvasModule.prototype = {
  4314.     /**
  4315.      * @param {!WebGLRenderingContext} glContext
  4316.      * @return {!Object}
  4317.      */
  4318.     wrapWebGLContext: function(glContext)
  4319.     {
  4320.         var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext);
  4321.         this._manager.registerResource(resource);
  4322.         return resource.proxyObject();
  4323.     },
  4324.  
  4325.     /**
  4326.      * @param {!CanvasRenderingContext2D} context
  4327.      * @return {!Object}
  4328.      */
  4329.     wrapCanvas2DContext: function(context)
  4330.     {
  4331.         var resource = Resource.forObject(context) || new CanvasRenderingContext2DResource(context);
  4332.         this._manager.registerResource(resource);
  4333.         return resource.proxyObject();
  4334.     },
  4335.  
  4336.     /**
  4337.      * @return {!CanvasAgent.TraceLogId}
  4338.      */
  4339.     captureFrame: function()
  4340.     {
  4341.         return this._callStartCapturingFunction(this._manager.captureFrame);
  4342.     },
  4343.  
  4344.     /**
  4345.      * @return {!CanvasAgent.TraceLogId}
  4346.      */
  4347.     startCapturing: function()
  4348.     {
  4349.         return this._callStartCapturingFunction(this._manager.startCapturing);
  4350.     },
  4351.  
  4352.     markFrameEnd: function()
  4353.     {
  4354.         this._manager.markFrameEnd();
  4355.     },
  4356.  
  4357.     /**
  4358.      * @param {function(this:ResourceTrackingManager)} func
  4359.      * @return {!CanvasAgent.TraceLogId}
  4360.      */
  4361.     _callStartCapturingFunction: function(func)
  4362.     {
  4363.         var oldTraceLog = this._manager.lastTraceLog();
  4364.         func.call(this._manager);
  4365.         var traceLog = /** @type {!TraceLog} */ (this._manager.lastTraceLog());
  4366.         if (traceLog === oldTraceLog) {
  4367.             for (var id in this._traceLogs) {
  4368.                 if (this._traceLogs[id] === traceLog)
  4369.                     return id;
  4370.             }
  4371.         }
  4372.         var id = this._makeTraceLogId();
  4373.         this._traceLogs[id] = traceLog;
  4374.         return id;
  4375.     },
  4376.  
  4377.     /**
  4378.      * @param {!CanvasAgent.TraceLogId} id
  4379.      */
  4380.     stopCapturing: function(id)
  4381.     {
  4382.         var traceLog = this._traceLogs[id];
  4383.         if (traceLog)
  4384.             this._manager.stopCapturing(traceLog);
  4385.     },
  4386.  
  4387.     /**
  4388.      * @param {!CanvasAgent.TraceLogId} id
  4389.      */
  4390.     dropTraceLog: function(id)
  4391.     {
  4392.         var traceLog = this._traceLogs[id];
  4393.         if (traceLog)
  4394.             this._manager.dropTraceLog(traceLog);
  4395.         delete this._traceLogs[id];
  4396.         delete this._traceLogPlayers[id];
  4397.         injectedScript.releaseObjectGroup(id);
  4398.     },
  4399.  
  4400.     /**
  4401.      * @param {!CanvasAgent.TraceLogId} id
  4402.      * @param {number=} startOffset
  4403.      * @param {number=} maxLength
  4404.      * @return {!CanvasAgent.TraceLog|string}
  4405.      */
  4406.     traceLog: function(id, startOffset, maxLength)
  4407.     {
  4408.         var traceLog = this._traceLogs[id];
  4409.         if (!traceLog)
  4410.             return "Error: Trace log with the given ID not found.";
  4411.  
  4412.         // Ensure last call ends a frame.
  4413.         traceLog.addFrameEndMark();
  4414.  
  4415.         var replayableCalls = traceLog.replayableCalls();
  4416.         if (typeof startOffset !== "number")
  4417.             startOffset = 0;
  4418.         if (typeof maxLength !== "number")
  4419.             maxLength = replayableCalls.length;
  4420.  
  4421.         var fromIndex = Math.max(0, startOffset);
  4422.         var toIndex = Math.min(replayableCalls.length - 1, fromIndex + maxLength - 1);
  4423.  
  4424.         var alive = this._manager.capturing() && this._manager.lastTraceLog() === traceLog;
  4425.         var result = {
  4426.             id: id,
  4427.             /** @type {!Array.<!CanvasAgent.Call>} */
  4428.             calls: [],
  4429.             /** @type {!Array.<!CanvasAgent.CallArgument>} */
  4430.             contexts: [],
  4431.             alive: alive,
  4432.             startOffset: fromIndex,
  4433.             totalAvailableCalls: replayableCalls.length
  4434.         };
  4435.         /** @type {!Object.<string, boolean>} */
  4436.         var contextIds = {};
  4437.         for (var i = fromIndex; i <= toIndex; ++i) {
  4438.             var call = replayableCalls[i];
  4439.             var resource = call.replayableResource();
  4440.             var contextResource = resource.contextResource();
  4441.             var stackTrace = call.stackTrace();
  4442.             var callFrame = stackTrace ? stackTrace.callFrame(0) || {} : {};
  4443.             var item = CallFormatter.forResource(resource).formatCall(call);
  4444.             item.contextId = CallFormatter.makeStringResourceId(contextResource.id());
  4445.             item.sourceURL = callFrame.sourceURL;
  4446.             item.lineNumber = callFrame.lineNumber;
  4447.             item.columnNumber = callFrame.columnNumber;
  4448.             item.isFrameEndCall = traceLog.isFrameEndCallAt(i);
  4449.             result.calls.push(item);
  4450.             if (!contextIds[item.contextId]) {
  4451.                 contextIds[item.contextId] = true;
  4452.                 result.contexts.push(CallFormatter.forResource(resource).formatValue(contextResource));
  4453.             }
  4454.         }
  4455.         return result;
  4456.     },
  4457.  
  4458.     /**
  4459.      * @param {!CanvasAgent.TraceLogId} traceLogId
  4460.      * @param {number} stepNo
  4461.      * @return {{resourceState: !CanvasAgent.ResourceState, replayTime: number}|string}
  4462.      */
  4463.     replayTraceLog: function(traceLogId, stepNo)
  4464.     {
  4465.         var traceLog = this._traceLogs[traceLogId];
  4466.         if (!traceLog)
  4467.             return "Error: Trace log with the given ID not found.";
  4468.         this._traceLogPlayers[traceLogId] = this._traceLogPlayers[traceLogId] || new TraceLogPlayer(traceLog);
  4469.         injectedScript.releaseObjectGroup(traceLogId);
  4470.  
  4471.         var replayResult = this._traceLogPlayers[traceLogId].stepTo(stepNo);
  4472.         var resource = replayResult.lastCall.resource();
  4473.         var dataURL = resource.toDataURL();
  4474.         if (!dataURL) {
  4475.             resource = resource.contextResource();
  4476.             dataURL = resource.toDataURL();
  4477.         }
  4478.         return {
  4479.             resourceState: this._makeResourceState(resource.id(), traceLogId, resource, dataURL),
  4480.             replayTime: replayResult.replayTime
  4481.         };
  4482.     },
  4483.  
  4484.     /**
  4485.      * @param {!CanvasAgent.TraceLogId} traceLogId
  4486.      * @param {!CanvasAgent.ResourceId} stringResourceId
  4487.      * @return {!CanvasAgent.ResourceState|string}
  4488.      */
  4489.     resourceState: function(traceLogId, stringResourceId)
  4490.     {
  4491.         var traceLog = this._traceLogs[traceLogId];
  4492.         if (!traceLog)
  4493.             return "Error: Trace log with the given ID not found.";
  4494.  
  4495.         var parsedStringId1 = this._parseStringId(traceLogId);
  4496.         var parsedStringId2 = this._parseStringId(stringResourceId);
  4497.         if (parsedStringId1.injectedScriptId !== parsedStringId2.injectedScriptId)
  4498.             return "Error: Both IDs must point to the same injected script.";
  4499.  
  4500.         var resourceId = parsedStringId2.resourceId;
  4501.         if (!resourceId)
  4502.             return "Error: Wrong resource ID: " + stringResourceId;
  4503.  
  4504.         var traceLogPlayer = this._traceLogPlayers[traceLogId];
  4505.         var resource = traceLogPlayer && traceLogPlayer.replayWorldResource(resourceId);
  4506.         return this._makeResourceState(resourceId, traceLogId, resource);
  4507.     },
  4508.  
  4509.     /**
  4510.      * @param {!CanvasAgent.TraceLogId} traceLogId
  4511.      * @param {number} callIndex
  4512.      * @param {number} argumentIndex
  4513.      * @param {string} objectGroup
  4514.      * @return {{result:(!RuntimeAgent.RemoteObject|undefined), resourceState:(!CanvasAgent.ResourceState|undefined)}|string}
  4515.      */
  4516.     evaluateTraceLogCallArgument: function(traceLogId, callIndex, argumentIndex, objectGroup)
  4517.     {
  4518.         var traceLog = this._traceLogs[traceLogId];
  4519.         if (!traceLog)
  4520.             return "Error: Trace log with the given ID not found.";
  4521.  
  4522.         var replayableCall = traceLog.replayableCalls()[callIndex];
  4523.         if (!replayableCall)
  4524.             return "Error: No call found at index " + callIndex;
  4525.  
  4526.         var value;
  4527.         if (replayableCall.isPropertySetter())
  4528.             value = replayableCall.propertyValue();
  4529.         else if (argumentIndex === -1)
  4530.             value = replayableCall.result();
  4531.         else {
  4532.             var args = replayableCall.args();
  4533.             if (argumentIndex < 0 || argumentIndex >= args.length)
  4534.                 return "Error: No argument found at index " + argumentIndex + " for call at index " + callIndex;
  4535.             value = args[argumentIndex];
  4536.         }
  4537.  
  4538.         if (value instanceof ReplayableResource) {
  4539.             var traceLogPlayer = this._traceLogPlayers[traceLogId];
  4540.             var resource = traceLogPlayer && traceLogPlayer.replayWorldResource(value.id());
  4541.             var resourceState = this._makeResourceState(value.id(), traceLogId, resource);
  4542.             return { resourceState: resourceState };
  4543.         }
  4544.  
  4545.         var remoteObject = injectedScript.wrapObject(value, objectGroup, true, false);
  4546.         return { result: remoteObject };
  4547.     },
  4548.  
  4549.     /**
  4550.      * @return {!CanvasAgent.TraceLogId}
  4551.      */
  4552.     _makeTraceLogId: function()
  4553.     {
  4554.         return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}";
  4555.     },
  4556.  
  4557.     /**
  4558.      * @param {number} resourceId
  4559.      * @param {!CanvasAgent.TraceLogId} traceLogId
  4560.      * @param {?Resource=} resource
  4561.      * @param {string=} overrideImageURL
  4562.      * @return {!CanvasAgent.ResourceState}
  4563.      */
  4564.     _makeResourceState: function(resourceId, traceLogId, resource, overrideImageURL)
  4565.     {
  4566.         var result = {
  4567.             id: CallFormatter.makeStringResourceId(resourceId),
  4568.             traceLogId: traceLogId
  4569.         };
  4570.         if (resource) {
  4571.             result.imageURL = overrideImageURL || resource.toDataURL();
  4572.             result.descriptors = CallFormatter.forResource(resource).formatResourceStateDescriptors(resource.currentState(), traceLogId);
  4573.         }
  4574.         return result;
  4575.     },
  4576.  
  4577.     /**
  4578.      * @param {string} stringId
  4579.      * @return {{injectedScriptId: number, traceLogId: ?number, resourceId: ?number}}
  4580.      */
  4581.     _parseStringId: function(stringId)
  4582.     {
  4583.         return /** @type {?} */ (InjectedScriptHost.eval("(" + stringId + ")"));
  4584.     }
  4585. }
  4586.  
  4587. var injectedCanvasModule = new InjectedCanvasModule();
  4588. return injectedCanvasModule;
  4589.  
  4590. })
  4591.